【问题标题】:Issue refreshing child components in React after implementing React DnD实现 React DnD 后在 React 中刷新子组件
【发布时间】:2020-05-14 17:18:19
【问题描述】:

我正在尝试使用 React DnD 在 React 中实现可排序列表的可排序列表。在实现拖放方面之前,一切都运行良好。

我有一个容器组件,它呈现这个:

<DndProvider backend={Backend}>
                    {this.state.classifications.map((classification, index) =>
                        <Classification id={classification.id} classification={classification} reportTemplate={this} key={classification.id} index={index} />
                    )}
</DndProvider>

分类扩展组件,构造如下:

   constructor(props) {
        super(props);

        this.state = {
            isEditing: false,
            classification: props.classification
        };
    }

...并呈现这个(为简洁起见):

<div className="panel-body">
    <DndProvider backend={Backend}>
        {this.state.classification.labels.map((label, index) =>
            <Label id={label.id} label={label} reportTemplate={this.props.reportTemplate} key={label.id} index={index} />
         )}
    </DndProvider>
</div>

反过来,Label 也扩展了组件,构造如下:

 constructor(props) {
        super(props);

        this.state = {
            isEditing: false,
            label: props.label
        };
    }

...并像这样渲染(再次为简洁起见):

return (
    <div className={"panel panel-default panel-label " + (isDragging ? "dragging " : "") + (isOver ? " over" : "")}>
        <div className="panel-heading" role="tab" id={"label-" + this.state.label.id}>
            <div className="panel-title">
                <div className="row">
                    <div className="col-xs-6 label-details">
                        {this.state.isEditing
                            ? <input type="text" className="form-control" value={this.state.label.value} onChange={e => this.props.reportTemplate.onLabelValueChange(e, this.state.label.classificationId, this.state.label.id, 'value')} />
                            : <p className="form-control-static">{this.state.label.value}</p>
                        }
                    <div className="subuser-container">...</div>
                </div>


            </div>
        </div>
    </div>
);

所有这些都运行良好 - 当用户从 Label 子组件进行更改时,它会在根组件中更新,并且所有内容都会同步并刷新。

然而,在实现 React DnD 时,Classification 和 Label 组件都被包裹在 Drag and Drop 装饰器中,以提供排序。通过拖放进行排序非常有效。 然而:这导致元素的更新停止工作(即,当从标签进行更改时,更新会正确地传递给根组件,但它不会随后刷新那个树)。

分类和标签dnd的实现在render方法中都是这样的:

return connectDropTarget(connectDragSource(...));

...导出组件时:

export default DropTarget('classification', classificationTarget, classificationDropCollect)(DragSource('classification', classificationSource, classificationDragCollect)(Classification));

有趣的是,当标签被编辑时,当用户拖放组件时会发生刷新。所以它就像拖放会触发组件刷新,但不会触发其他onChange函数。

这是一个很长的问题,抱歉。我几乎可以肯定其他人会遇到此问题,因此非常感谢任何指针。

【问题讨论】:

  • 你使用的是状态变量,有时候这个可能不会刷新,你直接用props试过了吗?
  • @vishnusandhireddy - 你的意思是在渲染中?如果是这样,我确实尝试过,它似乎有效果。根组件会更新,刷新只会由拖放交互触发。
  • 在构造函数中,您将 props.label 复制到 this.state.label 但是当您更改父级中的标签并且父级传递一个新的 props.label 时,您的组件会忽略它,因为它只复制到 this.state在构造函数中。你可以不使用 this.state.label 而是使用 props.label。
  • 另一个可能出现的问题是onLabelValueChange 改变了状态但是父组件也不会渲染。
  • @HMR - 感谢您的建议,非常感谢。我确实追求了这条道路,但没有任何结果。请记住,在使用 DropTarget 和 DragSource 装饰组件之前,这一切都有效。因此,我想知道是否是那些装饰器中的某些东西以某种方式阻止了刷新。

标签: reactjs react-dnd


【解决方案1】:

好的,我基本上已经回答了我自己的问题,但非常感谢那些在这里发布 cmets 以帮助缩小范围的人。

答案是我的状态对象既复杂又深刻,React DnD 的组件/装饰器实现似乎对此存在问题。我的假设是装饰器的shouldComponentUpdate 中有一些行为会在更新深度属性时阻止组件刷新。 React DnD 自己的文档将装饰器称为“遗留”,这很公平。

我更新了我们的组件以使用钩子而不是装饰器,这一切都变得栩栩如生。所以答案是这样的,如果你的 DnD 实现又深又复杂,那就使用钩子。

【讨论】:

    【解决方案2】:

    这是一个没有 DnD 的代码示例,它也不起作用,因为 prop 被复制到构造函数中的 state 并且在并发渲染时,prop 不再使用,只有 state。

    在 Child 中,您可以看到它会渲染但计数器永远不会改变,在 CorrectChild 中,计数器会改变,因为它只是使用 props.counter。

    class Child extends React.Component {
      constructor(props) {
        super(props);
        this.state = {//copied only once in constructor
          counterFromParent: props.counter,
        };
      }
      rendered=0;
      render() {
        this.rendered++;
        return (
          <div>
            <h3>in broken Child rendered {this.rendered} times</h3>
            <button onClick={this.props.up}>UP</button>
            <pre>
              {JSON.stringify(this.state, undefined, 2)}
            </pre>
          </div>
        );
      }
    }
    class CorrectChild extends React.Component {
      render() {
        //not copying props.count, just using it
        return (
          <div>
            <h3>in correct Child</h3>
            <button onClick={this.props.up}>UP</button>
            <pre>
              {JSON.stringify(this.props, undefined, 2)}
            </pre>
          </div>
        );
      }
    }
    function App() {
      const [state, setState] = React.useState({ counter: 1 });
      const up = React.useCallback(
        () =>
          setState((state) => ({
            ...state,
            counter: state.counter + 1,
          })),
        []
      );
      return (
        <div>
          <h3>state in App:</h3>
          <pre>{JSON.stringify(state, undefined, 2)}</pre>
          <Child counter={state.counter} up={up} />
          <CorrectChild counter={state.counter} up={up} />
        </div>
      );
    }
    ReactDOM.render(<App />, document.getElementById('root'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    
    
    <div id="root"></div>

    如果您在没有 DnD 的情况下仍然遇到这种情况,您的代码“工作”,那么请提供一个“不工作”的 Minimal, Reproducible Example

    【讨论】:

    • 感谢您对此的帮助 - 您的 cmets 帮助我验证了问题确实存在于其他地方。
    猜你喜欢
    • 2021-11-25
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多