问题描述
功能描述: 我尝试制作的 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 (将#修改为@)