ReactJS: setState on parent inside child component

Reactjs

Reactjs Problem Overview


What is the recommended pattern for doing a setState on a parent from a child component.

var Todos = React.createClass({
  getInitialState: function() {
    return {
      todos: [
        "I am done",
        "I am not done"
      ]
    }
  },

  render: function() {
    var todos = this.state.todos.map(function(todo) {
      return <div>{todo}</div>;
    });

    return <div>
      <h3>Todo(s)</h3>
      {todos}
      <TodoForm />
    </div>;
  }
});

var TodoForm = React.createClass({
  getInitialState: function() {
    return {
      todoInput: ""
    }
  },

  handleOnChange: function(e) {
    e.preventDefault();
    this.setState({todoInput: e.target.value});
  },

  handleClick: function(e) {
    e.preventDefault();
    //add the new todo item
  },

  render: function() {
    return <div>
      <br />
      <input type="text" value={this.state.todoInput} onChange={this.handleOnChange} />
      <button onClick={this.handleClick}>Add Todo</button>
    </div>;
  }
});

React.render(<Todos />, document.body)

I have an array of todo items which is maintained in the parent's state. I want to access the parent's state and add a new todo item, from the TodoForm's handleClick component. My idea is to do a setState on the parent, which will render the newly added todo item.

Reactjs Solutions


Solution 1 - Reactjs

In your parent, you can create a function like addTodoItem which will do the required setState and then pass that function as props to the child component.

var Todos = React.createClass({

  ...

  addTodoItem: function(todoItem) {
    this.setState(({ todos }) => ({ todos: { ...todos, todoItem } }));
  },

  render: function() {

    ...

    return <div>
      <h3>Todo(s)</h3>
      {todos}
      <TodoForm addTodoItem={this.addTodoItem} />
    </div>
  }
});

var TodoForm = React.createClass({
  handleClick: function(e) {
    e.preventDefault();
    this.props.addTodoItem(this.state.todoInput);
    this.setState({todoInput: ""});
  },

  ...

});

You can invoke addTodoItem in TodoForm's handleClick. This will do a setState on the parent which will render the newly added todo item. Hope you get the idea.

Fiddle here.

Solution 2 - Reactjs

For those who are maintaining state with the React Hook useState, I adapted the above suggestions to make a demo slider App below. In the demo app, the child slider component maintains the parent's state.

The demo also uses useEffect hook. (and less importantly, useRef hook)

import React, { useState, useEffect, useCallback, useRef } from "react";

//the parent react component
function Parent() {

  // the parentState will be set by its child slider component
  const [parentState, setParentState] = useState(0);

  // make wrapper function to give child
  const wrapperSetParentState = useCallback(val => {
    setParentState(val);
  }, [setParentState]);

  return (
    <div style={{ margin: 30 }}>
      <Child
        parentState={parentState}
        parentStateSetter={wrapperSetParentState}
      />
      <div>Parent State: {parentState}</div>
    </div>
  );
};

//the child react component
function Child({parentStateSetter}) {
  const childRef = useRef();
  const [childState, setChildState] = useState(0);

  useEffect(() => {
    parentStateSetter(childState);
  }, [parentStateSetter, childState]);

  const onSliderChangeHandler = e => {
  //pass slider's event value to child's state
    setChildState(e.target.value);
  };

  return (
    <div>
      <input
        type="range"
        min="1"
        max="255"
        value={childState}
        ref={childRef}
        onChange={onSliderChangeHandler}
      ></input>
    </div>
  );
};

export default Parent;

Solution 3 - Reactjs

These are all essentially correct, I just thought I would point to the new(ish) official react documentation which basically recommends:-

> There should be a single “source of truth” for any data that changes in a React application. Usually, the state is first added to the component that needs it for rendering. Then, if other components also need it, you can lift it up to their closest common ancestor. Instead of trying to sync the state between different components, you should rely on the top-down data flow.

See https://reactjs.org/docs/lifting-state-up.html. The page also works through an example.

Solution 4 - Reactjs

You could create an addTodo function in the parent component, bind it to that context, pass it to the child component and call it from there.

// in Todos
addTodo: function(newTodo) {
    // add todo
}

Then, in Todos.render, you would do

<TodoForm addToDo={this.addTodo.bind(this)} />

Call this in TodoForm with

this.props.addToDo(newTodo);

Solution 5 - Reactjs

parentSetState={(obj) => { this.setState(obj) }}

Solution 6 - Reactjs

I found the following working and simple solution to pass arguments from a child component to the parent component:

//ChildExt component
class ChildExt extends React.Component {
	render() {
		var	handleForUpdate	=	this.props.handleForUpdate;
		return (<div><button onClick={() => handleForUpdate('someNewVar')}>Push me</button></div>
		)
	}
}

//Parent component
class ParentExt extends React.Component {	
	constructor(props) {
		super(props);
		var handleForUpdate	= this.handleForUpdate.bind(this);
	}
	handleForUpdate(someArg){
			alert('We pass argument from Child to Parent: \n' + someArg);   
	}

	render() {
		var	handleForUpdate	=	this.handleForUpdate;    
		return (<div>
					<ChildExt handleForUpdate = {handleForUpdate.bind(this)} /></div>)
	}
}

if(document.querySelector("#demo")){
	ReactDOM.render(
		<ParentExt />,
		document.querySelector("#demo")
	);
}

Look at JSFIDDLE

Solution 7 - Reactjs

If you are working with a class component as parent, one very simple way of passing a setState to a child is by passing it within an arrow function. This works as it sets a hoisted environment that can be passed around:

class ClassComponent ... {

    modifyState = () =>{
        this.setState({...})   
    }
    render(){
          return <><ChildComponent parentStateModifier={modifyState} /></>
    }
}

Solution 8 - Reactjs

For anyone using useState inside Arrow Functional Components with TypeScript enabled, here's a simple example of how you can pass the parent's state setter function to the child, and invoke it to set the parent's state at the appropriate time from the child-

Sample

Parent component:

import {useState} from "react";
const ParentComponent = () => {
  const [hasParentStateChange, setHasParentStateChange] = useState<boolean>(false);

  return (
      <div>
        Parent's view: {String(hasParentStateChange)}
        <ChildComponent
            hasParentStateChange={hasParentStateChange}
            setHasParentStateChange={setHasParentStateChange} // <---- Passing parent's state setter over
        />
      </div>
  );
}

Child component:

interface PropsDefinition {
  hasParentStateChange: boolean;
  setHasParentStateChange(data: boolean): void;
}

const ChildComponent = (props: PropsDefinition) => {
  return (
      <div>
        <div>
          Child's view: {String(props.hasParentStateChange)}
        </div>
        <button
            onClick={() => props.setHasParentStateChange(!props.hasParentStateChange)} // <---- Invoking parent's state setter
        >
            Toggle parent state from child
        </button>
      </div>
  );
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPavithraView Question on Stackoverflow
Solution 1 - ReactjsDeepakView Answer on Stackoverflow
Solution 2 - ReactjsNicoWheatView Answer on Stackoverflow
Solution 3 - ReactjsTattyFromMelbourneView Answer on Stackoverflow
Solution 4 - ReactjsrallrallView Answer on Stackoverflow
Solution 5 - ReactjsdezmanView Answer on Stackoverflow
Solution 6 - ReactjsRomanView Answer on Stackoverflow
Solution 7 - ReactjsJulio PereiraView Answer on Stackoverflow
Solution 8 - Reactjsdian jinView Answer on Stackoverflow