Comment mettre à jour l'état du parent dans React?
Ma structure se présente comme suit:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
Le composant 3 doit afficher certaines données en fonction de l'état du composant 5. Puisque les accessoires sont immuables, Je ne peux pas simplement enregistrer son état dans le composant 1 et le transmettre, non? Et oui, j'ai lu à propos de redux, mais je ne veux pas l'utiliser. J'espère qu'il est possible de le résoudre juste avec react. Suis-je tort?
7 réponses
Pour la communication enfant-parent, vous devez passer une fonction définissant l'état du parent à l'enfant, comme ceci
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
}
handler(e) {
e.preventDefault()
this.setState({
someVar: someValue
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {this.props.handler}/ >
}
}
De cette façon, l'enfant peut mettre à jour l'état du parent avec l'appel d'une fonction passée avec des accessoires.
Mais vous devrez repenser la structure de vos composants, car si je comprends bien les composants 5 et 3 ne sont pas liés.
Une solution possible est de les envelopper d'un niveau supérieur qui contient l'état des deux composante 1 et 3. Ce composant définira l'état de niveau inférieur à travers les accessoires.
J'ai trouvé la solution de travail suivante pour passer l'argument de la fonction onClick de l'enfant au composant parent:
La Version avec le passage d'une méthode()
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
var arg1 = '';
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: ' + someArg);
this.setState({arg1:someArg});
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Version avec passage D'une fonction flèche
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
}
handleToUpdate = (someArg) => {
alert('We pass argument from Child to Parent: ' + someArg);
}
render() {
return (<div>
<ChildB handleToUpdate = {this.handleToUpdate} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
J'aime la réponse concernant les fonctions de passage, c'est une technique très pratique.
D'un autre côté, vous pouvez également y parvenir en utilisant pub/sub ou en utilisant une variante, un répartiteur, comme le fait Flux. La théorie est super simple, demandez au composant 5 d'envoyer un message que le composant 3 écoute. Le composant 3 met alors à jour son état qui déclenche le re-rendu. Cela nécessite des composants avec État, qui, selon votre point de vue, peuvent ou non être un anti-modèle. Je suis contre eux personnellement et préféreraient que quelque chose d'autre écoute les dépêches et change d'état de haut en bas (Redux le fait, mais ajoute une terminologie supplémentaire).
import { Dispatcher } from flux
import { Component } from React
const dispatcher = new Dispatcher()
// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
state = {
text: 'foo'
}
componentDidMount() {
dispatcher.register( dispatch => {
if ( dispatch.type === 'change' ) {
this.setState({ text: 'bar' })
}
}
}
render() {
return <h1>{ this.state.text }</h1>
}
}
// Click handler
const onClick = event => {
dispatcher.dispatch({
type: 'change'
})
}
// Component 5 in your example
const StatelessChild = props => {
return <button onClick={ onClick }>Click me</button>
}
Le répartiteur regroupe avec Flux est très simple, il enregistre simplement les rappels et les appelle quand une expédition se produit, en passant par le contenu de L'expédition (dans l'exemple laconique ci-dessus, il n'y a pas de payload
avec l'expédition, simplement un id de message). Vous pouvez l'adapter au pub/sub traditionnel (par exemple en utilisant le EventEmitter des événements, ou une autre version) très facilement si cela a plus de sens pour vous.
J'ai trouvé la solution de travail suivante pour passer l'argument de la fonction onClick de l'enfant au composant parent avec param:
Classe parente:
class Parent extends React.Component {
constructor(props) {
super(props)
// Bind the this context to the handler function
this.handler = this.handler.bind(this);
// Set some state
this.state = {
messageShown: false
};
}
// This method will be sent to the child component
handler(param1) {
console.log(param1);
this.setState({
messageShown: true
});
}
// Render the child component and set the action property with the handler as value
render() {
return <Child action={this.handler} />
}}
Classe enfant:
class Child extends React.Component {
render() {
return (
<div>
{/* The button will execute the handler function set by the parent component */}
<Button onClick={this.props.action.bind(this,param1)} />
</div>
)
} }
Lorsque vous avez besoin de communiquer entre enfant et parent à n'importe quel niveau, il est préférable d'utiliser context. Dans le composant parent, définissez le contexte qui peut être invoqué par l'enfant, tel que
Dans le composant parent dans votre cas composant 3
static childContextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
getChildContext() {
return {
parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
};
}
parentMethod(parameter_from_child){
// update the state with parameter_from_child
}
Maintenant, dans le composant enfant (composant 5 dans votre cas), dites simplement ceci composant qu'il veut utiliser le contexte de son parent.
static contextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
render(){
return(
<TouchableHighlight
onPress={() =>this.context.parentMethod(new_state_value)}
underlayColor='gray' >
<Text> update state in parent component </Text>
</TouchableHighlight>
)}
Vous pouvez trouver le projet de démonstration à repo
- nous pouvons créer ParentComponent et avec la méthode handleInputChange pour mettre à jour L'état ParentComponent. Importez le ChildComponent et nous passons deux accessoires du composant parent à l'enfant, c'est-à-dire.handleInputChange fonction et Compter.
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.state = {
count: '',
};
}
handleInputChange(e) {
const { value, name } = e.target;
this.setState({ [name]: value });
}
render() {
const { count } = this.state;
return (
<ChildComponent count={count} handleInputChange={this.handleInputChange} />
);
}
}
-
Maintenant, nous créons le fichier ChildComponent et enregistrons en tant que ChildComponent.jsx. Ce composant est sans état car le composant enfant n'a pas d'état. Nous utilisons la bibliothèque prop-types pour la vérification des types d'accessoires.
import React from 'react'; import { func, number } from 'prop-types'; const ChildComponent = ({ handleInputChange, count }) => ( <input onChange={handleInputChange} value={count} name="count" /> ); ChildComponent.propTypes = { count: number, handleInputChange: func.isRequired, }; ChildComponent.defaultProps = { count: 0, }; export default ChildComponent;
<Footer
action={()=>this.setState({showChart: true})}
/>
<footer className="row">
<button type="button" onClick={this.props.action}>Edit</button>
{console.log(this.props)}
</footer>
Try this example to write inline setState, it avoids creating another function.