![](/img/trans.png)
[英]React: Updating / appending an array in state from a functional component
[英]Updating & appending to React array state
我有一个用户创建编码问题的表单。 在表单中,可以选择通过输入和 output 文本框添加示例测试用例。 用户可以单击一个按钮来添加一个新的测试用例。 现在我有一个 state object 包含所有表单数据 formObj,其中有一个 sample_test_cases 字段,我想保存一个对象数组,例如:[{ input: "", Z78E6226:"1393D1366"1393D13666
我遇到的问题是更新这个数组。 每次添加测试用例时,我都需要能够将新的 object 连接到它。 然后在更改文本框时更新该索引处的 state。 我尝试创建一个无状态数组并对其进行更新,然后将 sample_test_cases 设置为该数组。 但是,这不起作用。
这是一个带有我的代码的沙箱: https://codesandbox.io/s/eloquent-snowflake-n4bpl?file=/src/AddProblemForm.js
如果有人能给我一些真正有用的提示。 我对 Javascript 或复杂的 state 管理不太熟悉。 谢谢。
请参阅下面的片段(我在保存沙箱时遇到问题)。 它解决的几个问题:
arr
变量跟踪 state; 更新不会触发重新渲染,因此您需要修改formObj
state 中的sample_test_cases
数组。value
也应该反映 state 中的内容。 为了更方便,我将测试用例作为道具传递到SampleTestCase
组件中,因此它将对 state 更改做出反应。sample_test_cases
数组,该数组由第一个 i-1 测试用例、一个带有修改输入的新第 i 个测试用例和剩余的测试用例构成.SampleTestCase
AddProblemForm
组件之外。 如果你不这样做,你会发现每当输入文本区域改变时,你就会失去键盘焦点。 这是因为SampleTestCase
组件在每次渲染时都被重新定义,这是由 state 更改触发的。 (类似问题: React.js - 重新渲染时输入失去焦点)import React, { useState } from "react";
import { Form, Button, Col } from "react-bootstrap";
import { BsPlusSquare } from "react-icons/bs";
const SampleTestCase = ({ testCase, updateInput }) => {
return (
<Form.Row>
<Col>
<Form.Group controlId="input">
<Form.Label>Sample Input</Form.Label>
<Form.Control
required
as="textarea"
rows={2}
onChange={updateInput}
value={testCase.input}
/>
</Form.Group>
</Col>
<Col>
<Form.Group controlId="output">
<Form.Label>Sample Output</Form.Label>
<Form.Control
required
as="textarea"
rows={2}
// onChange={(event) =>
// setFormObj({
// ...formObj,
// sample_test_cases: {
// ...formObj.sample_test_cases,
// output: event.target.value
// }
// })
// }
/>
</Form.Group>
</Col>
</Form.Row>
);
};
const AddProblemForm = () => {
const [formObj, setFormObj] = useState({
sample_test_cases: [{ input: "", output: "" }]
// other state obj info
});
const AddSampleTestCase = () => {
// make new instance!
const newTestCase = { input: "", output: "" };
setFormObj({
...formObj,
sample_test_cases: [...formObj.sample_test_cases, newTestCase]
});
};
console.log(formObj);
const updateInputFor = (i) => (event) => {
event.preventDefault();
const { sample_test_cases } = formObj;
const testCase = sample_test_cases[i];
testCase.input = event.target.value;
setFormObj({
...formObj,
sample_test_cases: [
...sample_test_cases.slice(0, i),
testCase,
...sample_test_cases.slice(i + 1)
]
});
};
return (
<div>
Problem Form
<Form>
<h5 style={{ paddingTop: "1rem" }}>Sample Test Cases</h5>
{formObj.sample_test_cases.map((testCase, i) => (
<React.Fragment key={i}>
<SampleTestCase
key={i}
testCase={testCase}
updateInput={updateInputFor(i)}
/>
<hr />
</React.Fragment>
))}
<Button onClick={AddSampleTestCase}>
<div>Add Sample Test Case</div>
</Button>
</Form>
</div>
);
};
export default AddProblemForm;
如果你的 state 很复杂,我建议你使用 useReducer 钩子。
当您有涉及多个子值的复杂 state 逻辑或下一个 state 取决于前一个时,useReducer 通常比 useState 更可取。
AddProblemForm.js :
import React, { useReducer } from "react";
import { Form, Button, Col } from "react-bootstrap";
import { BsPlusSquare } from "react-icons/bs";
import SampleTestCase from "./SampleTestCase";
const initialState = {
sample_test_cases: [],
counter: 0
// other state obj info
};
function reducer(state, action) {
switch (action.type) {
case "addSampleTestCase": {
const { data } = action;
return {
...state,
sample_test_cases: [...state.sample_test_cases, data],
counter: state.counter + 1
};
}
case "updateTest": {
const { index, value } = action;
return {
...state,
sample_test_cases: state.sample_test_cases.map((item, i) => {
if (i === index) {
return value;
} else {
return item;
}
})
};
}
default:
throw new Error();
}
}
const AddProblemForm = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const AddSampleTestCase = () => {
dispatch({ type: "addSampleTestCase", data: { input: "", output: "" } });
};
/* console.log(state); */
return (
<div>
Problem Form
<Form>
<h5 style={{ paddingTop: "1rem" }}>Sample Test Cases</h5>
{state.sample_test_cases.map((sample_test_case, i) => (
<div key={i}>
<SampleTestCase
sample_test_case={sample_test_case}
updateValue={(value) =>
dispatch({ type: "updateTest", index: i, value })
}
/>
<hr />
</div>
))}
<Button onClick={AddSampleTestCase}>
<div>Add Sample Test Case</div>
</Button>
</Form>
</div>
);
};
export default AddProblemForm;
SampleTestCase.js :
import React from "react";
import { Form, Button, Col } from "react-bootstrap";
const SampleTestCase = ({ sample_test_case, updateValue }) => {
return (
<Form.Row>
<Col>
<Form.Group controlId="input">
<Form.Label>Sample Input</Form.Label>
<Form.Control
required
as="textarea"
rows={2}
value={sample_test_case.input}
onChange={(event) => updateValue(event.target.value)}
/>
</Form.Group>
</Col>
<Col>
<Form.Group controlId="output">
<Form.Label>Sample Output</Form.Label>
<Form.Control
required
as="textarea"
rows={2}
value={sample_test_case.output}
onChange={(event) => updateValue(event.target.value)}
/>
</Form.Group>
</Col>
</Form.Row>
);
};
export default SampleTestCase;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.