ReactJS - 页面重新呈现但一个组件保持不变。 (漂亮的拖放)

ReactJS - Page re-renders but one component stays the same. (Beautiful Drag&Drop)

我遇到了一个非常奇怪的问题,页面上只有一个组件没有刷新,我就是想不通为什么。

这是该问题的简短视频:

https://i.gyazo.com/45e229b0867c37e48a18da7a55afb522.mp4

请注意当我单击确认时问题字符串发生了怎样的变化(应该如此),但拖放卡片 window 保持不变。它一直显示问题名称“yooo”和答案“abc,def”,而这仅对第一个问题有效。

我对 ReactJS 还是比较陌生,所以这里可能有一些我不熟悉的功能?据我所知,下一个问题应该完全重新渲染 DragAndDrop。目前构造函数没有被再次调用,它以某种方式保存了上一题的数据。

此页面的渲染。此处调用 DragAndDrop。在 confirm() 中,currentQuestion 被设置为下一个问题。

return (
    <div>
      <h3>{currentQuestion.question}</h3>

      <DragAndDrop
        answers={currentQuestion.answers}
      />

      <Button onClick={() => confirm()}>Confirm</Button>
    </div>
  );

整个DragAndDrop.js 对不起代码墙,它几乎与Beautiful-DND的示例代码相同https://codesandbox.io/s/k260nyxq9v

/* eslint-disable no-console */
/* eslint-disable react/prop-types */
import React, { Component } from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";

// STYLING
const grid = 8;

const getItemStyle = (isDragging, draggableStyle) => ({
  // some basic styles to make the items look a bit nicer
  userSelect: "none",
  padding: grid * 2,
  margin: `0 0 ${grid}px 0`,

  // change background colour if dragging
  background: isDragging ? "cyan" : "white",

  // styles we need to apply on draggables
  ...draggableStyle,
});

const getListStyle = (isDraggingOver) => ({
  background: isDraggingOver ? "lightblue" : "lightgrey",
  padding: grid,
  width: "100%",
});

// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

export default class DragAndDrop extends Component {
  constructor(props) {
    super(props);
    this.state = {
      items: props.answers,
    };
    this.onDragEnd = this.onDragEnd.bind(this);

    console.log("Answers & items");
    console.log(this.props.answers);
    console.log(this.state.items);
  }

  onDragEnd(result) {
    // dropped outside list
    if (!result.destination) {
      return;
    }

    const items = reorder(
      this.state.items,
      result.source.index,
      result.destination.index
    );

    this.setState({
      items,
    });
  }
  render() {
    return (
      <DragDropContext onDragEnd={this.onDragEnd}>
        <Droppable droppableId="droppable">
          {(provided, snapshot) => (
            <div
              {...provided.droppableProps}
              ref={provided.innerRef}
              style={getListStyle(snapshot.isDraggingOver)}
            >
              {this.state.items.map((item, index) => (
                <Draggable
                  key={item.id}
                  draggableId={item.id.toString()}
                  index={index}
                >
                  {(provided, snapshot) => (
                    <div
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                      style={getItemStyle(
                        snapshot.isDragging,
                        provided.draggableProps.style
                      )}
                    >
                      {
                        item.answer +
                          " index: " +
                          index +
                          " ordering:" +
                          item.ordering /*CONTENT OF CARD*/
                      }
                    </div>
                  )}
                </Draggable>
              ))}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    );
  }
}

我认为问题出在constructor中的这一行:

this.state = {
  items: props.answers,
};

在构造函数中像这样设置 items 意味着您将忽略父组件对 props 的任何后续更新!如果您检查 official documentation,他们会警告您不要这样做。

Avoid copying props into state! This is a common mistake:

The problem is that it’s both unnecessary (you can use this.props.color directly instead), and creates bugs (updates to the color prop won’t be reflected in the state).

Only use this pattern if you intentionally want to ignore prop updates. In that case, it makes sense to rename the prop to be called initialColor or defaultColor. You can then force a component to “reset” its internal state by changing its key when necessary.

如果你想依赖 props 的值并相应地改变状态,你可以使用 static getDerivedStateFromProps()

这里有一个 working example using a class-based component which is just a proof-of-concept using your component with static getDerivedStateFromProps() (which is not deprecated!). I added some dummy data that uses the same structure you provided in the parent component which changes when you click "Confirm". Also, here is a working example using hooks doing the same thing which uses useState and useEffect 钩子。

props.answers 由您的父组件控制,并确认 function.Your DragAndDrop 组件仅在构造函数中将 props 设置为初始状态。它不知道 props 何时更改,因为只有第一次在构造函数中将 props 设置为 state。您可以通过以下多种方式模拟道具变化:

  1. 随着 props 变化模拟状态变化
...
constructor(props) {
    this.state = {items:props.answers}
...
}
componentDidUpdate(prevProps, PrevState) {
    if (this.props.answers && this.props.answers !== prevProps.answers) {
      this.setState({ items: this.props.answers });
    }
  }
  1. 直接使用 props,构造函数中或 DragAndDrop 组件中的任何地方都没有状态

  2. 直接在 DragAndDrop 组件中移动道具和confirm