How to Pass Data Between Reactjs Components?

Passing data between React components is a fundamental part of building React applications. Here are several common methods to achieve this:

1. Props

Props are the primary way to pass data from a parent component to a child component.

// ParentComponent.js
import React from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const data = "Hello from Parent!";
  return <ChildComponent message={data} />;
};

export default ParentComponent;

// ChildComponent.js
import React from 'react';

const ChildComponent = ({ message }) => {
  return <div>{message}</div>;
};

export default ChildComponent;

2. State Lifting

When two sibling components need to share data, you can lift the state up to their common ancestor and pass it down as props.

// ParentComponent.js
import React, { useState } from 'react';
import Sibling1 from './Sibling1';
import Sibling2 from './Sibling2';

const ParentComponent = () => {
  const [sharedData, setSharedData] = useState("Shared Data");

  return (
    <div>
      <Sibling1 data={sharedData} />
      <Sibling2 setData={setSharedData} />
    </div>
  );
};

export default ParentComponent;

// Sibling1.js
import React from 'react';

const Sibling1 = ({ data }) => {
  return <div>{data}</div>;
};

export default Sibling1;

// Sibling2.js
import React from 'react';

const Sibling2 = ({ setData }) => {
  return <button onClick={() => setData("New Data")}>Update Data</button>;
};

export default Sibling2;

3. Context API

The Context API allows you to share data globally across your component tree without having to pass props down manually at every level.

// MyContext.js
import React, { createContext, useState } from 'react';

export const MyContext = createContext();

export const MyProvider = ({ children }) => {
  const [data, setData] = useState("Context Data");

  return (
    <MyContext.Provider value={{ data, setData }}>
      {children}
    </MyContext.Provider>
  );
};

// ParentComponent.js
import React from 'react';
import { MyProvider } from './MyContext';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  return (
    <MyProvider>
      <ChildComponent />
    </MyProvider>
  );
};

export default ParentComponent;

// ChildComponent.js
import React, { useContext } from 'react';
import { MyContext } from './MyContext';

const ChildComponent = () => {
  const { data, setData } = useContext(MyContext);

  return (
    <div>
      <p>{data}</p>
      <button onClick={() => setData("Updated Context Data")}>Update Data</button>
    </div>
  );
};

export default ChildComponent;

4. Custom Hooks

Custom hooks can encapsulate shared logic and data and be used across different components.

// useSharedData.js
import { useState } from 'react';

const useSharedData = () => {
  const [data, setData] = useState("Shared Data via Hook");

  return { data, setData };
};

export default useSharedData;

// ParentComponent.js
import React from 'react';
import useSharedData from './useSharedData';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const sharedData = useSharedData();

  return <ChildComponent sharedData={sharedData} />;
};

export default ParentComponent;

// ChildComponent.js
import React from 'react';

const ChildComponent = ({ sharedData }) => {
  const { data, setData } = sharedData;

  return (
    <div>
      <p>{data}</p>
      <button onClick={() => setData("Updated Data via Hook")}>Update Data</button>
    </div>
  );
};

export default ChildComponent;

5. Third-Party State Management Libraries

Libraries like Redux or MobX provide robust solutions for managing global state across an application.

Redux Example

// store.js
import { createStore } from 'redux';

const initialState = { data: "Redux Data" };

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'UPDATE_DATA':
      return { ...state, data: action.payload };
    default:
      return state;
  }
};

export const store = createStore(reducer);

// ParentComponent.js
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  return (
    <Provider store={store}>
      <ChildComponent />
    </Provider>
  );
};

export default ParentComponent;

// ChildComponent.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

const ChildComponent = () => {
  const data = useSelector((state) => state.data);
  const dispatch = useDispatch();

  return (
    <div>
      <p>{data}</p>
      <button onClick={() => dispatch({ type: 'UPDATE_DATA', payload: "Updated Redux Data" })}>Update Data</button>
    </div>
  );
};

export default ChildComponent;

These methods provide flexibility in how you manage and share data across your React components, depending on the complexity and requirements of your application.