为什么多个反应中对象的所有输入字段在复制到多个并以嵌套状态存储时都会链接和更改美丽的 DND 列表

问题描述

功能描述: 我尝试制作的 react 应用程序的目的是拥有预定义的食谱(在右侧),这些食谱的副本可以通过 react-beautiful-dnd 拖放到模块中。原始配方始终保留在配方列表中(图片 a、b)。可以使用相应的按钮再次删除模块中的配方。也可以添加和删除模块。食谱的数据是固定的。模块中的副本保存在状态中,每个副本都可以通过输入字段单独更改。

Functionality and Problems image

问题: 模块中的配方名称(状态的顶级层次结构:配方 1、配方 2、配方 3)可以按照预期的方式单独更改(图像 b、c、红色和绿色)。但是,如果其中一个值(处于状态的子层次结构中的数组中的一个对象:value0、value1、value2)发生更改,则模块和配方中的每个实例中具有相同值的所有字段都会更改本身(图像 b,c 橙色)。 预期行为应该是这些值的行为方式与配方名称的行为方式相同,并且仅针对更改值的特定实例进行更改。

不知何故,相同值的所有值字段似乎是链接在一起的,并且当只更改一个字段时,所有字段都会更改。问题是我为什么以及如何解决这个问题。

非常感谢您的想法。我还有一个沙盒版本的代码: https://codesandbox.io/s/nervous-chebyshev-kmy2w?file=/src/App.js

代码: index.js

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,rootElement
);

styles.css

.App {
  font-family: sans-serif;
  text-align: center;}   
.flex-container {
  display: flex;
  flex-wrap: wrap;}    
.cardrecipe {
  width: 500px;
  border: 1px solid;
  margin: 3px;}   
.accordionselect {
  width: 480px;
  margin-top: 1px;
  background-color: #423e3e !important;
  border: 1px solid;}   
.padding {padding: 10px;}   
.droparea {border: 1px solid;}

App.js

import React from "react";
import { v4 as uuid } from "uuid";
import { DragDropContext,Droppable,Draggable } from "react-beautiful-dnd";
import "./styles.css";

const reorder = (list,startIndex,endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex,1);
  result.splice(endIndex,removed);
  return result;
};

const copy = (source,destination,droppableSource,droppableDestination) => {
  const sourceClone = Array.from(source);
  const destClone = Array.from(destination);
  const item = sourceClone[droppableSource.index];
  destClone.splice(droppableDestination.index,{ ...item,id: uuid() });
  return destClone;
};

class App extends React.Component {
  data = [
    { name: "recipe1",data: [{ value: 10 },{ value: 20 },{ value: 30 }] },{ name: "recipe2",data: [{ value: 100 },{ value: 200 },{ value: 300 }] },{
      name: "recipe3",data: [{ value: 1000 },{ value: 2000 },{ value: 3000 }]
    }
  ];

  constructor() {
    super();
    this.state = {
      recipes: [],module: {
        [uuid()]: {
          mrecipes: []
        }
      },loading: false
    };
    this.addList = this.addList.bind(this);
    this.onDragEnd = this.onDragEnd.bind(this);
    this.handleChangesub = this.handleChangesub.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.delRecipe = this.delRecipe.bind(this);
    this.delModule = this.delModule.bind(this);
  }

  componentDidMount() {
    this._isMounted = true;
    this.setState({ loading: true });
    this.setState({
      recipes: this.data.map((item) => ({
        name: item.name,id: uuid(),data: item.data
      })),loading: false
    });
  }

  onDragEnd = (result) => {
    const { source,destination } = result;

    if (!destination) {
      return;
    }
    switch (source.droppableId) {
      case destination.droppableId:
        this.setState((prevState) => ({
          ...prevState,module: {
            ...prevState.module,[destination.droppableId]: {
              ...prevState.module[destination.droppableId],mrecipes: reorder(
                this.state.module[destination.droppableId].mrecipes,source.index,destination.index
              )
            }
          }
        }));
        break;
      case "ITEMS":
        this.setState((prevState) => ({
          ...prevState,mrecipes: copy(
                this.state.recipes,this.state.module[destination.droppableId].mrecipes,source,destination
              )
            }
          }
        }));
        break;
    }
  };

  addList = (e) => {
    this.setState((prevState) => ({
      ...prevState,module: {
        ...prevState.module,[uuid()]: {
          mrecipes: []
        }
      }
    }));
  };

  handleChange = (list,index) => (event) => {
    let inputValue = event.target.value;
    let statusCopy = Object.assign({},this.state);
    statusCopy.module[list].mrecipes[index].name = inputValue;
    this.setState(statusCopy);
  };

  handleChangesub = (list,index,iproc) => (event) => {
    let inputValue = event.target.value;
    let statusCopy = Object.assign({},this.state);
    statusCopy.module[list].mrecipes[index].data[iproc].value = inputValue;
    this.setState(statusCopy);
  };

  delRecipe = (list,index) => {
    this.setState((state) => {
      const newList = state.module[list].mrecipes.splice(index,1);
      return { newList };
    });
  };

  delModule = (list) => {
    let state = { ...this.state };
    delete state.module[list];
    this.setState(state);
  };

  componentWillUnmount() {
    this._isMounted = false;
  }

  // Normally you would want to split things out into separate components.
  // But in this example everything is just done in one place for simplicity
  render() {
    return (
      <div>
        <div className="flex-container">
          <DragDropContext onDragEnd={this.onDragEnd}>
            <div className="cardrecipe">
              {Object.keys(this.state.module).map((list,i) => (
                <Droppable key={list} droppableId={list}>
                  {(provided,snapshot) => (
                    <div ref={provided.innerRef} className="droparea">
                      <h2>Module</h2>
                      <div>
                        <button onClick={() => this.delModule(list)}>
                          delete Module
                        </button>
                      </div>
                      <br />
                      <div>{"<drop area>"}</div>
                      <br />

                      {this.state.module[list].mrecipes.length
                        ? this.state.module[list].mrecipes.map(
                            (item,index) => (
                              <Draggable
                                key={list + index}
                                draggableId={list + index}
                                index={index}
                              >
                                {(provided,snapshot) => (
                                  <div ref={provided.innerRef}>
                                    <div
                                      {...provided.draggableProps}
                                      style={provided.draggableProps.style}
                                      {...provided.dragHandleProps}
                                      className={
                                        snapshot.isDragging
                                          ? "dragging"
                                          : "accordionselect"
                                      }
                                    >
                                      <input
                                        className="editorinput"
                                        id={"standard-basic" + index}
                                        label="Value"
                                        type="text"
                                        name={"value" + index}
                                        onChange={this.handleChange(
                                          list,index
                                        )}
                                        value={item.name}
                                      />
                                      <table>
                                        <tbody>
                                          {item.data.map((proc,iproc) => (
                                            <tr key={list + index + iproc}>
                                              <td>{"value" + index}</td>
                                              <td>
                                                <input
                                                  className="editorinput"
                                                  id={
                                                    "standard-basic" +
                                                    index +
                                                    iproc
                                                  }
                                                  label="Value"
                                                  type="text"
                                                  name={"value" + index + iproc}
                                                  onChange={this.handleChangesub(
                                                    list,iproc
                                                  )}
                                                  value={proc.value}
                                                />
                                              </td>
                                            </tr>
                                          ))}
                                        </tbody>
                                      </table>
                                      <div>
                                        <button
                                          onClick={() =>
                                            this.delRecipe(list,index)
                                          }
                                        >
                                          delete Recipe
                                        </button>
                                      </div>
                                    </div>
                                  </div>
                                )}
                              </Draggable>
                            )
                          )
                        : !provided.placeholder && <div>Drop items here</div>}
                      {provided.placeholder}
                    </div>
                  )}
                </Droppable>
              ))}
            </div>

            <div className="cardrecipe">
              <h2>Recipes</h2>
              <Droppable droppableId="ITEMS" isDropDisabled={true}>
                {(provided,snapshot) => (
                  <div ref={provided.innerRef} className="droparea">
                    {this.state.recipes.map((rec,idx) => (
                      <Draggable key={rec.id} draggableId={rec.id} index={idx}>
                        {(provided,snapshot) => (
                          <React.Fragment>
                            <div ref={provided.innerRef}>
                              <div
                                {...provided.draggableProps}
                                {...provided.dragHandleProps}
                                style={provided.draggableProps.style}
                                className={
                                  snapshot.isDragging
                                    ? "dragging"
                                    : "accordionselect"
                                }
                              >
                                <div key={rec.idx}>
                                  <div>{rec.name}</div>
                                  <table>
                                    <tbody>
                                      {rec.data.map((da,ida) => (
                                        <tr key={rec + idx + ida}>
                                          <td>{"value" + ida}</td>
                                          <td>
                                            <div>{da.value}</div>
                                          </td>
                                        </tr>
                                      ))}
                                    </tbody>
                                  </table>
                                </div>
                              </div>
                            </div>
                            {snapshot.isDragging && <div>{rec.name}</div>}
                          </React.Fragment>
                        )}
                      </Draggable>
                    ))}
                    {provided.placeholder}
                  </div>
                )}
              </Droppable>
            </div>
          </DragDropContext>
        </div>
        <div className="padding">
          <button onClick={this.addList}>Add Module</button>
        </div>
      </div>
    );
  }
}

export default App;

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)