【问题标题】:Updating & appending to React array state更新和附加到 React 数组状态
【发布时间】:2021-07-04 17:26:04
【问题描述】:

我有一个用户创建编码问题的表单。在表单中,可以选择通过输入和输出文本框添加示例测试用例。用户可以单击一个按钮来添加一个新的测试用例。现在我有一个包含所有表单数据的状态对象,formObj,其中有一个 sample_test_cases 字段,我想保存一个对象数组,例如:[{ 输入:“”,输出:“”}]。

我遇到的问题是更新这个数组。每次添加测试用例时,我都需要能够将新对象连接到它。然后在更改文本框时更新该索引处的状态。我尝试创建一个无状态数组并对其进行更新,然后将 sample_test_cases 设置为该数组。但是,这不起作用。

这是一个带有我的代码的沙盒:https://codesandbox.io/s/eloquent-snowflake-n4bpl?file=/src/AddProblemForm.js

如果有人能给我一些真正有用的建议。我不太熟悉 Javascript 或复杂的状态管理。谢谢。

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    请参阅下面的 sn-p(我在保存沙盒时遇到问题)。它解决的几个问题:

    1. 您还使用一些本地arr 变量来跟踪状态;更新不会触发重新渲染,因此您需要修改 formObj 状态中的 sample_test_cases 数组。
    2. textarea value 也应该反映您的状态。为了更方便,我将测试用例作为道具传递到SampleTestCase 组件中,这样它就会对状态变化做出反应。
    3. 反应状态应该被视为不可变的。所以当输入文本框被更新时,你应该用一个new sample_test_cases数组将状态设置为一个new对象,这个数组是由第一个i-1测试构成的用例、个带有修改输入的测试用例,以及剩余的测试用例。
    4. SampleTestCase 移出AddProblemForm 组件。如果你不这样做,你会发现每当输入文本区域改变时,你就会失去键盘焦点。这是因为SampleTestCase 组件在每次渲染 上都被重新定义,这是由状态更改触发的。 (类似问题:React.js - input losing focus when rerendering
    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;
    

    【讨论】:

    【解决方案2】:

    如果你的状态很复杂,我建议你使用 useReducer 钩子。

    hooks API

    当你有复杂的情况时,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;
    

    【讨论】:

      猜你喜欢
      • 2021-01-14
      • 1970-01-01
      • 2020-09-06
      • 1970-01-01
      • 2020-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-06
      相关资源
      最近更新 更多