【问题标题】:Force a child component to update when the parent state changes当父状态改变时强制子组件更新
【发布时间】:2018-01-22 18:17:21
【问题描述】:

我有一个MyList 组件,它可以获取项目,允许过滤和排序。该组件已在应用程序的其他部分使用,并且运行良好。它使用渲染道具来渲染项目,因此它接受 renderItem 函数类型的道具。

现在我正在构建一个简单的列表以允许使用上述组件选择项目,并且我正在检查渲染道具 renderItem 方法中的选定状态。问题是当我更改MySelectableList 的状态时,MyList 组件不会更新,因为它的道具不会改变(它始终是相同的绑定函数renderProp)。现在我用this.renderItem = this.renderItem.bind(this); 强制渲染子组件,但我不喜欢它,我知道我可以用ref 更新子组件,但我也不喜欢它。

当父状态改变时,有没有更好的方法来强制子组件渲染?我做错了吗?

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

提前致谢。

编辑

MyList 组件使用connect 连接到redux store。我发现connectMyList 组件缺少渲染的原因,仅使用“vanilla”反应组件它可以正常工作。

我在这个代码框里重现了这个问题:https://codesandbox.io/s/0mov14nmmp

【问题讨论】:

  • 为什么会重新渲染?父母和孩子之间没有约束力吗?您没有将state 传递为props
  • 不是将renderItem作为props传递,你可以不使用传递它作为孩子&lt;MyList&gt; &lt;MySelectableItem &gt; &lt;/MyList&gt;
  • @nitte93user3232918 感谢您的意见。我知道它不会重新渲染并且我理解为什么(this.renderItem 不会改变),我在问是否有一个很好的方法(更友好的反应)来触发渲染,或者我是否必须以某种方式改变整个设计。我不能将MySelectedItem 作为子组件传递,因为实际项目由子组件MyList 管理。

标签: javascript reactjs react-redux


【解决方案1】:

既然你问如何做到这一点,反应更友好

更好的方法是:

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

然后您的 MyList 将如下所示:

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

这看起来更具可读性、可维护性和易于调试。但我相信这对你来说都是显而易见的。因为,你问过a react friendly way,这是你能做的最友好的反应方式。

我不会建议不必要的,明确地尝试渲染任何组件。直到和除非它是唯一的方法,而在您的组件中并非如此。

【讨论】:

  • 谢谢,我理解您的建议,但我的项目由MyList 获取和管理,因此我无法实例化,也无法访问父范围内的item
【解决方案2】:

您实现 MyList 的方式没有任何问题。 React Native FlatList 具有相同的模式。但是你为什么不也将项目作为属性传递给 MyList,所以它会像

&lt;MyList items={this.state. selectedItems} renderItem={this.renderItem} /&gt;

这样 MyList 将重新渲染,因为 items 属性发生变化。 items 属性也是必需的,因为我假设在您的 MyList 组件中您需要执行 items.map 功能对吗?否则你怎么知道总共需要渲染多少个项目?

【讨论】:

  • 感谢您的回复,很遗憾items是由MyList组件直接获取和管理的,我不知道父组件中的项目。
猜你喜欢
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-02
  • 2021-06-30
  • 2019-01-08
  • 2021-08-11
相关资源
最近更新 更多