29

I have two react components and I'd like to call setState to set a state in the one Component but called in the other one. How do I do that?

Dennis Schweizer
  • 393
  • 1
  • 3
  • 6

2 Answers2

58

If you work with functional components you can use hooks like useState. Don't forget to "save" (memoize) the reference of your handler with useCallback, it helps React avoid useless rerenders.

Functional component solution

// myContainer.js
import React, { useState } from 'react'
import MyChild from 'some/path/myChild'

function MyContainer() {
  const [name, setName] = useState('foo')

  return (
    <MyChild name={name} onNameChange={setName} />
  )
}

export default MyContainer

// myChild.js
import React, { useCallback } from 'react'

function MyChild({ name, onNameChange }) {

  const handleInputChange = useCallback(event => {
    onNameChange(event.target.value)
  }, [onNameChange])

  return (
    <div>
      <input type="text" onChange={handleInputChange} value={name} />
      <div>The name is: {name}</div>
    </div>
  )
}

export default MyChild

In a class you can use handler (method) that contains some logic or function call(s). It help to keep your code maintainable.

Class component solution

// myContainer.js
import React, { Component } from 'react'
import MyChild from 'some/path/myChild'

class MyContainer extends Component {
  state = {
    name: 'foo'
  }

  handleNameChange = name => {
    this.setState({ name })
  }

  render() {
    return (
      <MyChild name={this.state.name} onNameChange={this.handleNameChange} />
    )
  }

}

export default MyContainer

// myChild.js
import React, { Component } from 'react'

class MyChild extends Component {

  handleInputChange = event => {
    this.props.onNameChange(event.target.value)
  }

  render() {
    return (
      <div>
        <input type="text" onChange={this.handleInputChange} value={this.props.name} />
        <div>The name is: {this.props.name}</div>
      </div>
    )
  }

}

export default MyChild
Alexandre Nicolas
  • 1,851
  • 17
  • 19
  • Hi, my situation is the inverse. Trying to change state in child functional component from a button click on the parent functional component. Also, there are multiple child components with a button each in a loop. How would I make sure only one of them updates? Thanks in advance! – Sanjiv Pradhanang Jun 18 '21 at 00:14
  • 1
    @SanjivPradhanang Perhaps you could open a question thread, expose your issue with code example. That would be easier to propose solutions. – Alexandre Nicolas Jun 24 '21 at 07:49
17

You can't directly call setState on a parent component from a child component because the updating of a component state is restricted to the current component.

To handle this, simply pass a function from the parent to the child that contains setState. So when you want to update the parent's state, just call that passed function.

A minimal example:

// Parent.jsx

import React, { Component } from 'react';
import Child from './Child';

class Parent extends Component {
  constructor(props) {
    super(props);
    this.setChanged = this.setChanged.bind(this);
    this.state = {
      changed: false
    }
  }

  // Function to set the parent's state
  setChanged() {
    this.setState({ changed: true });
  }

  render() {
    return <Child setChanged={this.setChanged} />
  }
}

// Child.js

import React from 'react';

function Child(props) {
  return (
    // When clicked, parent's setChanged function is called
    <div onClick={() => props.setChanged()} />
  )
}
Adam
  • 3,829
  • 1
  • 21
  • 31