ChatGPT解决这个技术问题 Extra ChatGPT

React js change child component's state from parent component

I have two components: Parent Component from which I want to change child component's state:

class ParentComponent extends Component {
  toggleChildMenu() {
    ?????????
  }
  render() {
    return (
      <div>
        <button onClick={toggleChildMenu.bind(this)}>
          Toggle Menu from Parent
        </button>
        <ChildComponent />
      </div>
    );
  }
}

And Child Component:

class ChildComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false;
    }
  }

  toggleMenu() {
    this.setState({
      open: !this.state.open
    });
  }

  render() {
    return (
      <Drawer open={this.state.open}/>
    );
  }
}

I need to either change Child Component's open state from Parent Component, or call Child Component's toggleMenu() from Parent Component when Button in Parent Component is clicked?

Maybe you can hold a child reference in parent, and change child's state explicitly,See this doc

p
pwolaq

The state should be managed in the parent component. You can transfer the open value to the child component by adding a property.

class ParentComponent extends Component {
   constructor(props) {
      super(props);
      this.state = {
        open: false
      };

      this.toggleChildMenu = this.toggleChildMenu.bind(this);
   }

   toggleChildMenu() {
      this.setState(state => ({
        open: !state.open
      }));
   }

   render() {
      return (
         <div>
           <button onClick={this.toggleChildMenu}>
              Toggle Menu from Parent
           </button>
           <ChildComponent open={this.state.open} />
         </div>
       );
    }
}

class ChildComponent extends Component {
    render() {
      return (
         <Drawer open={this.props.open}/>
      );
    }
}

Yeah that is essentially what the react-classnames package does, but it also allows you to always apply a set of classnames, and conditionally apply others. Like this: classNames({ foo: true, bar: this.props.open }); // => 'foo' when this.props.open = false and 'foo bar' when this.props.open = true.
How can we change the open state in child component?
you can add a property toggle to the ChildComponent <ChildComponent open={this.state.open} toggle={this.toggleChildMenu.bind(this)} /> and call this.props.toggle() in the child component
I don't understand, you can call it wherever you want in the child component as soon as you specified this property when declaring the ChildComponent -> <ChildComponent toggle={this.toggleChildMenu.bind(this)} />
In this way, whole Parent component is being re-render by which we loose efficiency. Can you please tell any way in which only Child component will update its State or Props (means re-render) without re-render of Parent component.
m
miguel savignano

The parent component can manage child state passing a prop to child and the child convert this prop in state using componentWillReceiveProps.

class ParentComponent extends Component {
  state = { drawerOpen: false }
  toggleChildMenu = () => {
    this.setState({ drawerOpen: !this.state.drawerOpen })
  }
  render() {
    return (
      <div>
        <button onClick={this.toggleChildMenu}>Toggle Menu from Parent</button>
        <ChildComponent drawerOpen={this.state.drawerOpen} />
      </div>
    )
  }
}

class ChildComponent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      open: false
    }
  }

  componentWillReceiveProps(props) {
    this.setState({ open: props.drawerOpen })
  }

  toggleMenu() {
    this.setState({
      open: !this.state.open
    })
  }

  render() {
    return <Drawer open={this.state.open} />
  }
}

in react 16 use getDerivedStateFromProps
@FadiAboMsalam I'm using react version 16.7.0 with @Types/react version 16.7.18. At least on the TypeScript side there doesn't seem to be getDerivedStateFromProps(). However, Miguel's answer suggesting to use componentWillReceiveProps(props) is available and worked like a charm in my env.
In this case, how would the toggleMenu() state change inside the child component would reach the parent? Imagine I close the drawer, how would the parent component know it's been closed?
J
Jaison James

Above answer is partially correct for me, but In my scenario, I want to set the value to a state, because I have used the value to show/toggle a modal. So I have used like below. Hope it will help someone.

class Child extends React.Component {
  state = {
    visible:false
  };

  handleCancel = (e) => {
      e.preventDefault();
      this.setState({ visible: false });
  };

  componentDidMount() {
    this.props.onRef(this)
  }

  componentWillUnmount() {
    this.props.onRef(undefined)
  }

  method() {
    this.setState({ visible: true });
  }

  render() {
    return (<Modal title="My title?" visible={this.state.visible} onCancel={this.handleCancel}>
      {"Content"}
    </Modal>)
  }
}

class Parent extends React.Component {
  onClick = () => {
    this.child.method() // do stuff
  }
  render() {
    return (
      <div>
        <Child onRef={ref => (this.child = ref)} />
        <button onClick={this.onClick}>Child.method()</button>
      </div>
    );
  }
}

Reference - https://github.com/kriasoft/react-starter-kit/issues/909#issuecomment-252969542


This is what I want, but I am wondering why not just use react refs? seedoc
What does the onRef prop do?
This is actually a discouraged pattern in react because it reverses information flow. If react has a single zen, it's data down, actions up. This is the concept that makes react apps so easy to reason about. Imperative handles can be useful but should be used sparingly and well-thought. They should not be the go-to solution for e.g. toggling visibility.
E
Evandro Pomatti

You can use the createRef to change the state of the child component from the parent component. Here are all the steps.

Create a method to change the state in the child component. Create a reference for the child component in parent component using React.createRef(). Attach reference with the child component using ref={}. Call the child component method using this.yor-reference.current.method.

Parent component


class ParentComponent extends Component {
constructor()
{
this.changeChild=React.createRef()
}
  render() {
    return (
      <div>
        <button onClick={this.changeChild.current.toggleMenu()}>
          Toggle Menu from Parent
        </button>
        <ChildComponent ref={this.changeChild} />
      </div>
    );
  }
}

Child Component


class ChildComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false;
    }
  }

  toggleMenu=() => {
    this.setState({
      open: !this.state.open
    });
  }

  render() {
    return (
      <Drawer open={this.state.open}/>
    );
  }
}




This is the best answer I have found, because it does not re-render the parent component. This is much faster and much better practice!
Yes this is actually the best answer too for my needs. That's very clean, we should upvote this answer.
On parent, I had to use a handler method instead of directly calling the child. Then it worked.
I
Idrees

If you are using functional components. You can achieve this by:

const ParentComponent = () => {
   const [isOpen, setIsOpen] = useState(false)

   toggleChildMenu() {
      setIsOpen(prevValue => !prevValue)
   }

   return (
     <div>
       <button onClick={toggleChildMenu}>
         Toggle Menu from Parent
       </button>
       <Child open={isOpen} />
     </div>
   );
}



const Child = ({open}) => {
  return (
    <Drawer open={open}/>
  );
}

How can the child in this case control open? If the child say has a button to close the drawer, how does this work out?
J
Juba Fourali

You can send a prop from the parent and use it in child component so you will base child's state changes on the sent prop changes and you can handle this by using getDerivedStateFromProps in the child component.