DEV Community

Cover image for Understanding Inverse Data Flow in React 💃🏻
isabel k.
isabel k.

Posted on

14 5

Understanding Inverse Data Flow in React 💃🏻

What is inverse data flow?

In React, inverse data flow allows us to send data between parent and child components as props, or properties. However, components that are cousins or siblings cannot directly communicate with each other.

Sharing data between parent and child components

Here's an example of inverse data flow between a parent component and a child component. Let's say we're building an app that allows users to create accounts by entering their email addresses.



class Home extends React.Component {
  state = {
    email_address: ""
  }

  handleChange = (inputFromChild) => {
    this.setState({
      email_address: inputFromChild
    })
  }

  handleResponse = (event) => {
    event.preventDefault()
    console.log("Something has been done.")
  }

  render () {
    return (
      <CreateAccountForm
        handleChange={this.handleChange}
        handleResponse={this.handleResponse}/>

      <AccountSettings
        handleChange={this.handleChange}
        handleResponse={this.handleResponse}/>
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

In our Home component, we're defining the handleChange() and handleResponse() functions and then sending them down as props to its child components, CreateAccountForm and AccountSettings. The information inputted by the user in these child components is then sent back up to the parent component by invoking those very same functions. This allows us to "reuse" these functions without having to copy and paste the same code in both of the child components.

If we didn't use props, here's what our components might look like:



class Home extends React.Component {
  state = {
    email_address: ""
  }

  render () {
    return (
      <CreateAccountForm />
      <AccountSettings />
    )
  }
}

class CreateAccountForm extends React.Component {
  handleChange = (inputFromChild) => {
    this.setState({
      email_address: inputFromChild
    })
  }

  handleResponse = (event) => {
    event.preventDefault()
    console.log("Something has been done.")
  }

  render () {
    return (
      <div>
        <form onSubmit={this.handleResponse}>
          <label>Email Address: </label>
          <input type="text" name="email_address" onChange={this.handleChange} />
          <input type="submit" value="Create Account" />
        </form>

      </div>
    )
  }
}

class AccountSettings extends React.Component {
  handleChange = (inputFromChild) => {
    this.setState({
      email_address: inputFromChild
    })
  }

  handleResponse = (event) => {
    event.preventDefault()
    console.log("Something has been done.")
  }

  render () {
    return (
      <div>
        <form onSubmit={this.handleResponse}>
          <label>Email Address: </label>
          <input type="text" name="email_address" onChange={this.handleChange} />
          <input type="submit" value="Create Account" />
        </form>

      </div>
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

This isn't very DRY, is it? It also makes things complicated if we want to update the handleChange() and handleReponse() functions in both places. Placing those two functions in the Home component and sending it down to its child components creates a single source of truth.

Limitations of inverse data flow

While inverse data flow is great for writing DRYer code, it can sometimes be too restrictive. For example, components that do not have a direct parent or child relationship cannot share props with each other.

Graphic of inverse data flow relationships

If we wrote a function called toggleFormVisibility() in our CreateAccountForm component, and we wanted to use it in our AccountSettings component, it would be not be available as a prop. In order to create access to that function, we would have to send it back up to the parent and back down to AccountSettings.



class CreateAccountForm extends React.Component {
  state = {
    displayForm: false
  }

  toggleFormVisibility = () => {
    this.setState({
      displayForm: !this.state.displayform
    })
  }

  render () {
    return (
      <div>
        <button onClick={this.toggleFormVisibility}>Show the form</button>

        <form onSubmit={this.props.handleResponse}>
          <label>Email Address: </label>
          <input type="text" name="email_address" onChange={this.props.handleChange} />
          <input type="submit" value="Create Account" />
        </form>

      </div>
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

This process of sharing data can become quite cumbersome and confusing to follow if there are several components with complex relationships.

Summary

1) Define the function in the parent component.



class Home extends React.Component {
  state = {
    email_address: ""
  }

  handleChange = (inputFromChild) => {
    this.setState({
      email_address: inputFromChild
    })
  }

  render () {
    return (
      <CreateAccountForm />
      <AccountSettings />
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

2) Send down the function as props to the child component.



class Home extends React.Component {
  ...

  render () {
    return (
      <CreateAccountForm handleChange={this.handleChange} />
      <AccountSettings handleChange={this.handleChange} />
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

3) Invoke the function in the child.
4) Send data back up to the parent as props.



class CreateAccountForm extends React.Component {
  render () {
    return (
      <div>
        <form>
          <label>Email Address: </label>
          <input type="text" name="email_address" onChange={this.props.handleChange} />
          <input type="submit" value="Create Account" />
        </form>

      </div>
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

5) Voila! You've just created inverse data flow.

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (1)

Collapse
 
mjoycemilburn profile image
MartinJ

Well done - the template in your "summary" could very usefully be incorporated in React's own "reactjs.org/docs/thinking-in-react... document. This just describes the problem in the abstract and will leave a lot of learners completely cold

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

👋 Kindness is contagious

If this post resonated with you, feel free to hit ❤️ or leave a quick comment to share your thoughts!

Okay