React: ways to pass data from child to parent components

·

4 min read

In React, it's common to pass data down the component tree from parent to child using props. However, sometimes you might need to pass state data back up the component tree from a child component to its parent. In this blog, we'll explore five different ways to pass state from child to parent components in React, along with examples for each method.

Parent function as Props

The simplest way to pass state data from a child component to its parent is by passing a callback function as a prop from the parent component to the child component. The child component can then call this callback function, passing the state data as an argument.

Here's an example:

import { useState } from 'react';

function Parent() {
  const [childState, setChildState] = useState(null);

  function handleChildStateChange(newState) {
    setChildState(newState);
  }

  return (
    <div>
      <Child onStateChange={handleChildStateChange} />
      <p>Child state: {childState}</p>
    </div>
  );
}

function Child(props) {
  const [state, setState] = useState('initial state');

  function handleClick() {
    const newState = 'updated state';
    setState(newState);
    props.onStateChange(newState);
  }

  return (
    <button onClick={handleClick}>
      Update state and pass to parent
    </button>
  );
}

Global State

Global state is a way to share state data between components without having to pass props down the component tree or use a context provider. There are several libraries that provide global state management, such as Redux and MobX. In this blog, we will use Recoil JS, a state management library developed by Facebook.

Recoil JS provides a simple and intuitive way to manage global state in a React application. It uses the concept of atoms, which are units of state that can be accessed and modified from any component in the application.

Here's an example:

import { atom, useRecoilState } from 'recoil';

const childState = atom({
  key: 'childState',
  default: 'initial state',
});

function Parent() {
  const [state, setState] = useRecoilState(childState);

  function handleChildStateChange(newState) {
    setState(newState);
  }

  return (
    <>
      <Child onStateChange={handleChildStateChange} />
      <p>Child state: {state}</p>
    </>
  );
}

function Child(props) {
  const [state, setState] = useRecoilState(childState);

  function handleClick() {
    const newState = 'updated state';
    setState(newState);
    props.onStateChange(newState);
  }

  return (
    <button onClick={handleClick}>
      Update state and pass to parent
    </button>
  );
}

JS Events

JavaScript events provide a way to communicate between components using the built-in browser Event API. This method involves creating a custom event in the child component that fires the event when the state changes. The parent component listens to this event and updates its state accordingly.

Here's an example:

function Parent() {
  const [state, setState] = useState('initial state');

  useEffect(() => {
    window.addEventListener('childStateChange', (event) => {
      setState(event.detail);
    });

    return () => {
      window.removeEventListener('childStateChange');
    };
  }, []);

  return (
    <>
      <Child />
      <p>Child state: {state}</p>
    </>
  );
}

function Child() {
  const [state, setState] = useState('initial state');

  function handleStateChange(newState) {
    setState(newState);
    const event = new CustomEvent('childStateChange', { detail: newState });
    window.dispatchEvent(event);
  }

  return (
    <>
      <button onClick={() => handleStateChange('updated state')}>
        Update state and dispatch event
      </button>
      <p>Child state: {state}</p>
    </>
  );
}

Similarly, you can also write a pub-sub functions

Here's an example:

const subscribers = {};

function subscribe(event, callback) {
  if (!subscribers[event]) {
    subscribers[event] = [];
  }
  subscribers[event].push(callback);
}

function publish(event, data) {
  if (!subscribers[event]) {
    return;
  }
  subscribers[event].forEach((callback) => {
    callback(data);
  });
}

In this example, we create two functions: subscribe and publish. The subscribe function allows a component to register a callback function for a specific event. The publish function allows a component to trigger an event and pass data to all subscribers.

Here's an example usage of these functions:

// component 1
subscribe('eventName', (data) => {
  console.log('Component 1 received:', data);
});

// component 2
subscribe('eventName', (data) => {
  console.log('Component 2 received:', data);
});

// component 3
publish('eventName', 'some data');

In this example, components 1 and 2 subscribe to an event called eventName and register a callback function that will be called when the event is published. Component 3 publishes an event with the name eventName and passes the string 'some data' as the payload. When the event is published, both components 1 and 2 receive the event and log the payload to the console.