Guedda
@Guedda
Начинающий front-end разработчик

Как обновить дочерний компонент из родительского?

Добрый день.
Суть вопроса такова:
Есть два компонента - родительский:
export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            someVar: 0
        }
    }

    setVar(some) {
        this.setState({someVar: some});
    }

   render() {
        return (<Child someVar={this.state.someVar} setVar={this.setVar.bind(this)} />);
   }
}

и дочерний:
export default class Child extends Component {
    constructor(props) {
        super(props);
        this.state = {
            someVar: 0,
        }
    }

    getNext() {
        this.setState({someVar: this.state.someVar + 1});
    }

    componentWillReceiveProps(nextProps) {
        this.setState({someVar: nextProps.someVar});
    }

    componentDidMount() {
        this.getNext();
    }

   render() {
       // это просто пример. всё немного сложнее. но не очень.
       let btn = (<button type="button" onClick={this.props.setVar.bind(this, this.state.someVar)}>Go</button>
       return (<div>{this.state.someVar}</div>);
   }
}

Так вот при нажатии на кнопку "Go", дочерний компонент не перерисовывается. Что я не так делаю?
Заранее спасибо за ответы.
  • Вопрос задан
  • 2547 просмотров
Решения вопроса 1
Guedda
@Guedda Автор вопроса
Начинающий front-end разработчик
Да, я пробовал сначала этот вариант, но он не помог. Немного переделал, но теперь появляется другая ошибка, связанная с этим вопросом. Видимо, стоит лучше описать код, который у меня есть - я постараюсь:
Родительский компонент:
export default class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            response: false,                           
            selfUser: undefined,                       
            someId: undefined,
        }
    }

    getUser() {
        var self = this;
        someAjax().then(function(user) {
            if (user) {
                self.setState({response: true, selfUser: user, someId: user.someId});
            } else {
                self.setState({response: true, selfUser: undefined, someId: undefined});
            }
        });
    }

    setId(id) {
        this.setState({someId: id});
    }

    componentDidMount() {
        this.getUser();
    }


    render() {
        if (this.state.response) {
            let page;
            if (this.state.selfUser) {
                        page = (
                            <Child
                                sometId={this.state.someId}
                                setId={this.setId.bind(this)} />
                        );
            } else {
                page = (<div>No user</div>)
            }
            
            return (
                <div>
                    {page}        
                </div>
            )
        } else {
            return (
                <div>No response</div>
            )
        }
    }

}


Дочерний компонент:
export default class Child extends Component {

    constructor(props) {
        super(props);

        this.state = {
            status: "none",
            someData: undefined,
        }
    }

    getData() {
        $.ajax({
            url: 'someUrl'
            type: 'GET',
            success: function(data) {
                 this.setState({ status: "success", someData: data});
            }
        });
    }

    goToNextPage(item) {
        this.props.setId(item.Id);
    }

    renderDataChild(item) {
        let goToDataChildPage = this.goToNextPage.bind(this, item);
        return (
            <div key={item.Id} className="brick main-shadow">
                <div className="brick-inner" onClick={goToDataChildPage }><strong>{item.Name}</strong></div>
            </div>
        );
    }

    componentDidMount() {
        this.getData();
    }

    render() {
        switch (this.state.status) {
            case "none":
                return (<p></p>);
            case "success":
                if (this.state.someData) {
                    let someData = this.state.someData.values;
                    let tens = someData.map(function(obj) {
                        return this.renderDataChild(obj);
                    }.bind(this));

                    return (
                        <div>
                            {tens}
                        </div>
                    );
                } else {
                    return (
                        <div>No data</div>
                    );
                }
        }
    }

Так вот теперь я первый раз получаю все, а когда меняю someId (нажимаю на какой-нибудь brick), то JS пишет ошибку
Uncaught TypeError: Cannot read property 'map' of undefined

ругаясь на строку
let tens = someData.map(function(obj) {
Что я не так делаю? Всё это связано с props родительского компонента
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@SuperOleg39ru
Front-end разработчик
Во первых, вы объявили переменную btn, но не возвращаете ее в render.

Во вторых, зачем объявлять в Child this.state свойство someVar, если оно доступно вам из this.props?

Нет необходимости биндить this Child на коллбэк App, потому что тогда props.setVar метод будет вызывать setState у Child - this.props.setVar.bind(this, this.state.someVar)}

Вкратце, React сам обновляет компонент, если изменились его props (что происходит через коллбэк родителя).

export default class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            someVar: 0
        }

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

    setVar(some) {
        this.setState({someVar: some});
    }

   render() {
        return (<Child someVar={this.state.someVar} setVar={this.setVar} />);
   }
}

export default class Child extends Component {
    constructor(props) {
        super(props);

        this.handleClick = this.handleClick.bind(this);
        this.getNext = this.getNext.bind(this);
    }

    getNext() {
        return this.props.someVar + 1;
    }

    handleClick() {
        this.props.setVar( this.getNext() )
    }

   render() {
       let btn = (<button type="button" onClick={this.handleClick}>Go</button>
       return (<div><btn /> {this.props.someVar}</div>);
   }
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы