React js muda o estado do componente filho do componente pai

94

Tenho dois componentes: Componente pai, do qual desejo alterar o estado do componente filho:

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

E componente filho :

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}/>
    );
  }
}

Eu preciso alterar o estado aberto do componente filho do componente pai ou chamar toggleMenu () do componente filho do componente pai quando o botão no componente pai é clicado?

Torayeff
fonte
Talvez você possa manter uma referência de criança no pai e alterar o estado da criança explicitamente. Veja este documento
Chaojun Zhong

Respostas:

122

O estado deve ser gerenciado no componente pai. Você pode transferir o openvalor para o componente filho adicionando uma propriedade.

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}/>
      );
    }
}
Olivier Boissé
fonte
Isso pode ser usado para controlar uma propriedade css como 'display'? como em, se meu prop 'open' contiver 'none' ou 'inline-block', o css display prop será atualizado?
deusofnull
2
Sim, é essencialmente o que o pacote react-classnames faz, mas também permite que você sempre aplique um conjunto de nomes de classe e aplique outros condicionalmente. Assim: classNames({ foo: true, bar: this.props.open });// => 'foo' quando this.props.open = false e 'foo bar' quando this.props.open = true.
deusofnull
1
Como podemos alterar o estado aberto no componente filho?
Priyabrata Atha
1
você pode adicionar uma propriedade toggleao ChildComponent <ChildComponent open={this.state.open} toggle={this.toggleChildMenu.bind(this)} />e chamar this.props.toggle()o componente filho
Olivier Boissé
1
Não entendo, você pode chamá-lo de onde quiser no componente filho assim que especificar essa propriedade ao declarar o ChildComponent-><ChildComponent toggle={this.toggleChildMenu.bind(this)} />
Olivier Boissé
25

O componente pai pode gerenciar o estado filho passando um prop para o filho e o filho converter esse prop no estado usando 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} />
  }
}
miguel savignano
fonte
1
no react 16, use getDerivedStateFromProps
Fadi Abo Msalam
1
@FadiAboMsalam Estou usando a versão react 16.7.0 com @ Types / react versão 16.7.18. Pelo menos no lado do TypeScript, não parece haver getDerivedStateFromProps(). No entanto, a resposta de Miguel sugerindo o uso componentWillReceiveProps(props)está disponível e funcionou como um encanto no meu env.
Manfred
Nesse caso, como a mudança de estado toggleMenu () dentro do componente filho alcançaria o pai? Imagine que eu fecho a gaveta, como o componente pai saberia que foi fechada?
norman123123
20

A resposta acima está parcialmente correta para mim, mas no meu cenário, quero definir o valor para um estado, porque usei o valor para mostrar / alternar um modal. Então eu usei como abaixo. Espero que ajude alguém.

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>
    );
  }
}

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

Jaison
fonte
2
Isso é o que eu quero, mas estou me perguntando por que não usar apenas react refs? ver doc
Chaojun Zhong
O que o prop onRef faz?
norman123123
1

Você pode enviar um prop do pai e usá-lo no componente filho para basear as mudanças de estado do filho nas mudanças do prop enviado e pode lidar com isso usando getDerivedStateFromProps no componente filho.

Juba Fourali
fonte
1

Você pode usar o createRef para alterar o estado do componente filho do componente pai. Aqui estão todas as etapas.

  1. Crie um método para alterar o estado no componente filho.

    2 - Crie uma referência para o componente filho no componente pai usando React.createRef ().

    3 - Anexe a referência com o componente filho usando ref = {}.

    4 - Chame o método do componente filho usando this.yor-reference.current.method.

Componente pai


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>
    );
  }
}

Componente Criança


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}/>
    );
  }
}


Pranay Kumar
fonte