DEV Community

Cover image for Data Flow in React
Mursal Furqan Kumbhar
Mursal Furqan Kumbhar

Posted on

Data Flow in React

Ciao Everyone 👋
In this article, we are going to discuss how data flows in react, in other words, how we can share data between components.

The way data is passed

In React, data transfer is essential for seamless component functionality. Utilizing props, information flows unidirectionally from parent-to-child components. This straightforward approach ensures that child components receive the needed data for rendering and functionality, embodying a fundamental principle in React's component communication.

Parent to Child | Props

Data is passed from parent components to child components through props.

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

const ParentComponent = () {
   // Data passed from parent to child
   const dataFromParent = "Hello from parent!";
   return <ChildComponent data={dataFromParent} />;
};

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

const ChildComponent = (props) {
   // Display data received from parent
   return <p>{props.data}</p>;
};
Enter fullscreen mode Exit fullscreen mode

Child to Parent | Callbacks

Child components communicate with their parent components by passing callback functions as props, lifting the state in the component hierarchy.

  • Parent Component
// ParentComponent.jsx
import React from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
    // State to hold data from ChildComponent
    const [childData, setChildData] = React.useState(null);

    // Callback to update parent state with data from ChildComponent
    const handleChildData = (dataFromChild) => {
        setChildData(dataFromChild);
    };

    return (
        <div>
            <p>Data from Child: {childData}</p>
            {/* ChildComponent with callback to send data to parent */}
            <ChildComponent onSendData={handleChildData} />
        </div>
    );
};
Enter fullscreen mode Exit fullscreen mode
  • Child Component
// ChildComponent.jsx
import React from 'react'

const ChildComponent = ({ onSendData }) => {
    // State to hold the child input
    const [childInput, setChildInput] = React.useState('')

    // Callback function to send data to parent
    const sendData = () => {
        onSendData(childInput)
    }

    return (
        <div>
            <input
                type="text"
                value={childInput}
                onChange={(e) => setChildInput(e.target.value)}
            />
            <button onClick={sendData}>Send Data</button>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Child to Child | Context API

Context API enables indirect communication between components, creating a shared context. It's useful for avoiding prop drilling and allowing components at different levels to access shared data.

// DataContext.js
import React from "react";

// Create a context for shared data
const DataContext = React.createContext();

// DataProvider component to manage shared state
export const DataProvider = ({ children }) => {
    // State to hold shared data
    const [sharedData, setSharedData] = React.useState(null);

    // Function to update shared data
    const updateSharedData = (data) => {
        setSharedData(data);
    };

    // Provide shared data and update function to the context
    return (
        <DataContext.Provider value={{ sharedData, updateSharedData }}>
            {children}
        </DataContext.Provider>
    );
};

// Custom hook to access the shared data and update function
export const useData = () => {
    const context = React.useContext(DataContext);
    if (!context) {
        throw new Error("useData must be used within a DataProvider");
    }
    return context;
};
Enter fullscreen mode Exit fullscreen mode

Image minute wait

Other Ways

In React, developers use custom hooks for reusable logic and event buses for independent event communication. Custom hooks enhance code modularity, while event buses allow components to emit and listen for events autonomously. Caution is essential to align these techniques with specific application needs.

Using Context API

The Context API is not limited to child-to-child communication.
It can also be employed for broader state management, providing a shared context accessible by multiple components.

State Management Libraries (e.g., Redux, MobX)

State management libraries centralize and manage application state, enabling communication between components regardless of their position in the component tree.

Custom Hooks

Another effective way to handle state logic and share data between components is through custom hooks. Custom hooks allow you to encapsulate and reuse stateful logic across different parts of your application. By creating a custom hook, you can extract the shared functionality and use it in various components, promoting a clean and modular code structure.

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

const useCustomData = () => {
  const [customData, setCustomData] = useState(null);

  const updateCustomData = (newData) => {
    setCustomData(newData);
  };

  return { customData, updateCustomData };
};

export default useCustomData;
Enter fullscreen mode Exit fullscreen mode

Components can then use this custom hook to manage their specific data and update functions.

// ComponentA.jsx
import React from 'react';
import useCustomData from './useCustomData';

const ComponentA = () => {
  const { customData, updateCustomData } = useCustomData();

  // ... component logic using customData

  return <button onClick={() => updateCustomData('New data from Component A')}>Update Data</button>;
};
Enter fullscreen mode Exit fullscreen mode

Event Bus

An event bus is a pattern where components can emit and listen for events without having a direct parent-child relationship. This pattern involves a central event bus that acts as a mediator, allowing components to communicate indirectly. While this approach should be used judiciously to avoid potential pitfalls, it can be useful in certain scenarios where a more flexible communication system is required.

// EventBus.js
import { EventEmitter } from 'events';

const eventBus = new EventEmitter();

export default eventBus;
Enter fullscreen mode Exit fullscreen mode

Components can then subscribe to events and emit them when necessary.

// ComponentB.jsx
import React, { useEffect } from 'react';
import eventBus from './EventBus';

const ComponentB = () => {
  useEffect(() => {
    const eventHandler = (data) => {
      // Handle the event data
      console.log('Event received in Component B:', data);
    };

    // Subscribe to the event
    eventBus.on('customEvent', eventHandler);

    // Clean up the subscription on component unmount
    return () => {
      eventBus.removeListener('customEvent', eventHandler);
    };
  }, []);

  return <div>Component B</div>;
};
Enter fullscreen mode Exit fullscreen mode

These additional approaches provide flexibility and adaptability in different scenarios, allowing developers to choose the most suitable method based on their specific application requirements.

Top comments (0)