Моя структура выглядит следующим образом:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
Компонент 3 должен отображать некоторые данные в зависимости от состояния компонента 5. Поскольку реквизит неизменен, я не могу просто сохранить его в компоненте 1 и переслать его, правильно? И да, я читал о редуксе, но не хочу его использовать. Надеюсь, что это можно решить, просто отреагируя. Я не прав?
Для общения между родителями и детьми вы должны передать функцию, устанавливающую состояние от родителя к ребенку, например:
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
}
handler() {
this.setState({
someVar: someValue
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {this.props.handler}/ >
}
}
Таким образом, ребенок может обновить родительское состояние с помощью вызова функции, переданной с помощью реквизита.
Но вам придется переосмыслить структуру ваших компонентов, потому что, как я понимаю, компоненты 5 и 3 не связаны между собой.
Одно из возможных решений - заключить их в компонент более высокого уровня, который будет содержать состояние обоих компонентов 1 и 3. Этот компонент будет устанавливать состояние более низкого уровня с помощью подпорок.
this.handler = this.handler.bind(this)
в конструктор, this
внутри функции- handler
будет ссылаться на замыкание функции, а не на класс. Если вы не хотите связывать все свои функции в конструкторе, есть еще два способа справиться с этим, используя функции стрелок. Вы можете просто написать обработчик клика как onClick={()=> this.setState(...)}
, или вы можете использовать инициализаторы свойств вместе с функциями стрелок, как описано здесь babeljs.io/blog/2015/06/07/ response-on-es6-plus в разделе «Функции стрелок»
Я нашел следующее рабочее решение для передачи аргумента функции onClick от дочернего к родительскому компоненту:
Версия с передачей метода()
//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")
);
}
Версия с передачей функции Arrow
//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")
);
}
<ChildB handleToUpdate = {handleToUpdate.bind(this)} />
Почему пришлось снова связываться?
this
вызывается внутри ребенка, this
относится к состоянию родителей , а не ребенка. Это закрытие во всей красе!
Мне нравится ответ о передаче функций вокруг, его очень удобный метод.
С обратной стороны вы также можете добиться этого, используя pub/sub или используя вариант, диспетчер, как Flux. Теория супер проста, компонент 5 отправляет сообщение, которое прослушивает компонент 3. Компонент 3 затем обновляет свое состояние, которое запускает повторную визуализацию. Для этого требуются компоненты с состоянием, которые, в зависимости от вашей точки зрения, могут быть или не быть анти-шаблоном. Я против них лично и предпочел бы, чтобы что-то еще слушало рассылки и изменения из состояния сверху вниз (Redux делает это, но добавляет дополнительную терминологию).
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>
}
Диспетчерские пакеты с Flux очень просты, он просто регистрирует обратные вызовы и вызывает их при любой отправке, проходящей через содержимое в диспетчере (в приведенном выше примере нет отправки payload
с отправкой, просто сообщение Я бы). Вы можете легко адаптировать его к традиционному паб/суб (например, с помощью EventEmitter из событий или какой-либо другой версии), если это имеет для вас больше смысла.
Я нашел следующее рабочее решение для передачи аргумента функции onClick из дочернего элемента в родительский компонент с параметром:
родительский класс:
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} />
}}
дочерний класс:
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>
)
} }
Когда вам требуется установить связь между дочерним и родительским на любом уровне вниз, лучше использовать контекст. В родительском компоненте определяют контекст, который может быть вызван дочерним элементом, например
В родительском компоненте в вашем случае компонент 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
}
Теперь в дочернем компоненте (компонент 5 в вашем случае) просто скажите это что он хочет использовать контекст своего родителя.
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>
)}
вы можете найти демонстрационный проект repo
-We может создать ParentComponent и с помощью метода handleInputChange для обновления состояния ParentComponent. Импортируйте ChildComponent, и мы передаем два реквизита от родительского к дочернему компоненту, т.е. функции и count.handleInputChange.
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} />
);
}
}
Теперь мы создаем файл ChildComponent и сохраняем как ChildComponent.jsx. Этот компонент не имеет состояния, потому что дочерний компонент не имеет состояния. Мы используем библиотеку prop-types для проверки типов реквизита.
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;
Я хочу поблагодарить наиболее одобренный ответ за то, что он дал мне представление о моей собственной проблеме, в основном, о ее изменении с помощью функции стрелки и передачи параметра из дочернего компонента:
class Parent extends React.Component {
constructor(props) {
super(props)
// without bind, replaced by arrow func below
}
handler = (val) => {
this.setState({
someVar: val
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {() => this.props.handler('the passing value')}/ >
}
}
Надеюсь, это поможет кому-то.
<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.