简体   繁体   English

当父状态更改时,强制子组件更新

[英]Force a child component to update when the parent state changes

I have a MyList component that fetches items, allows filtering and sorting. 我有一个MyList组件,该组件可获取项目,允许进行过滤和排序。 This component is already used in other parts of the apps and it works well. 该组件已在应用程序的其他部分使用,并且运行良好。 It uses render props to render the items so it accepts a renderItem prop of type function. 它使用渲染道具渲染项目,因此它接受类型函数的renderItem道具。

Now I'm building a simple list to allow item selection using the aforementioned component and I'm checking for the selected state in the render prop renderItem method. 现在,我正在构建一个简单的列表,以允许使用上述组件进行项目选择,并且正在检查render prop renderItem方法中的选定状态。 The problem is that when I change the state of MySelectableList the MyList component doesn't update because its props does not change (it's always the same bound function renderProp ). 问题是,当我的状态改变MySelectableListMyList组件没有更新,因为它的道具不会改变(它总是相同的绑定功能renderProp )。 For now I forced the rendering of the child with this.renderItem = this.renderItem.bind(this); 现在,我用this.renderItem = this.renderItem.bind(this);强制渲染孩子this.renderItem = this.renderItem.bind(this); but I don't like it, I know that I can update the child component with ref but I don't like it either. 但是我不喜欢它,我知道我可以用ref更新子组件,但是我也不喜欢。

Is there a better method to force the child component to render when the parent state changes? 有没有更好的方法在父状态更改时强制子组件呈现? Am I doing something wrong? 难道我做错了什么?

Full code of MySelectableList : MySelectableList完整代码:

class MySelectableList extend Component {
    constructor (props) {
        super(props);

        this.state = {
            selectedItems: [],
        };
        this.renderItem = this.renderItem.bind(this);
        this.toggle = this.toggle.bind(this);
        this.isSelected = this.isSelected.bind(this);
    }

    toggle (item) {
        const newItems = this.state.selectedItems.slice(0);
        const index = newItems.indexOf(item.uuid);
        if (index === -1) {
            newItems.push(item.uuid);
        } else {
            newItems.splice(index, 1);
        }
        this.setState({ selectedItems: newItems });
        // Force MyList to re-render by tricking react that it's different
        this.renderItem = this.renderItem.bind(this);
    }

    isSelected (item) {
        return this.state.selectedItems.includes(item.uuid);
    }

    renderItem (item) {
        return (<MySelectableItem
            key={ item.uuid }
            item={ item }
            toggle={ this.toggle }
            selected={ this.isSelected(item) } />);
    }

    render () {
        return (
            <div>
                ...
                <MyList renderItem={ this.renderItem } />
                ...
            </div>
        );
    }
}

Thanks in advance. 提前致谢。

EDIT 编辑

The MyList component is connected to redux store using connect . MyList组件使用connect连接到redux存储。 I discovered that connect is the cause of the MyList component missing rendering, using only "vanilla" react component it works correctly. 我发现connectMyList组件缺少渲染的原因,仅使用“香草”反应组件即可正常工作。

I reproduced the problem in this codesandbox: https://codesandbox.io/s/0mov14nmmp 我在以下codeandbox中重现了该问题: https ://codesandbox.io/s/0mov14nmmp

Since you asked about how to do it more react friendly way 既然你问过如何做到更友好的反应方式

The nicer way to do this would be: 更好的方法是:

render () {
        return (
            <div>
                <MyList {...whateeverExtraPropsyouWantToPass}>
                   <MySelectableItem
                       key={ item.uuid }
                       item={ item }
                       toggle={ this.toggle }
                       selected={ this.isSelected(item) } />
                </MyList>
            </div>
        );

Then your MyList will look something like this: 然后,您的MyList将如下所示:

render () {
        return (
            <div>
               ...//your other MyList code
               ...
               {this.props.children}
            </div>
        );

This looks more readable, more maintainable and easily debuggble. 这看起来更易读,更可维护且易于调试。 But I'm sure this is all obvious to you. 但是我敢肯定,这对您来说是显而易见的。 Since, you asked about a react friendly way , this is the most react friendly way you can do. 既然您询问了a react friendly way ,这就是您可以做的最友好的反应方式。

I would not suggest unnecessary, explicitly trying to render any component. 我不会建议不必要的,明确尝试渲染任何组件。 Until and unless it is the only way, which is not the case in your component. 直到并且除非这是唯一的方法,否则组件中的情况并非如此。

There is nothing wrong with the way you implement MyList. 实现MyList的方式没有错。 React Native FlatList has the same pattern. React Native FlatList具有相同的模式。 But why dont you also pass items as a property to the MyList, so it will be like 但是为什么不将项目也作为属性传递给MyList,所以它将像

<MyList items={this.state. selectedItems} renderItem={this.renderItem} />

This way MyList will re-render because items property changes. 这样,由于items属性更改,MyList将重新呈现。 items property is needed as well because I assume that in your MyList component you need to do items.map function right? 还需要items属性,因为我认为您需要在MyList组件中执行items.map函数,对吗? otherwise how do you know how many items in total you need to render? 否则,您如何知道总共需要渲染多少个项目?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM