Javascript 更新及;附加到React数组状态

Javascript 更新及;附加到React数组状态,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我有一个表单,其中用户创建了一个编码问题。在表单中,可以通过输入和输出文本框添加示例测试用例。用户可以单击按钮添加新的测试用例。现在我有一个state对象,它保存所有表单数据formObj,其中有一个示例_test_cases字段,我想保存一个对象数组,比如:[{input:,output::}] 我遇到的问题是更新此阵列。我需要能够在每次添加测试用例时将一个新对象连接到它。然后在文本框更改时更新该索引处的状态。我尝试创建一个无状态数组并对其进行更新,然后将sample\u test\u案例设置

我有一个表单,其中用户创建了一个编码问题。在表单中,可以通过输入和输出文本框添加示例测试用例。用户可以单击按钮添加新的测试用例。现在我有一个state对象,它保存所有表单数据formObj,其中有一个示例_test_cases字段,我想保存一个对象数组,比如:[{input:,output::}]

我遇到的问题是更新此阵列。我需要能够在每次添加测试用例时将一个新对象连接到它。然后在文本框更改时更新该索引处的状态。我尝试创建一个无状态数组并对其进行更新,然后将sample\u test\u案例设置到该数组中。然而,这是行不通的

这是一个包含我的代码的沙盒:

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

请参阅下面的片段(我在保存沙箱时遇到问题)。它解决的几个问题:

  • 您还使用一些本地
    arr
    变量跟踪状态;更新不会触发重新渲染,因此您需要修改
    formObj
    状态中的
    sample\u test\u cases
    数组
  • textarea
    也应该反映您的状态。为了方便起见,我将测试用例作为道具传递到
    SampleTestCase
    组件中,因此它将对状态更改做出反应
  • 应将React状态视为不可变。因此,当更新输入文本框时,您应该使用新的
    sample\u test\u cases
    数组将状态设置为一个新对象,该数组由第一个i-1测试用例、一个修改了输入的新的第i个测试用例和剩余的测试用例构成
  • SampleTestCase
    移到
    AddProblemForm
    组件之外。如果你不这样做,你会发现每当输入文本区域被改变时,你就会失去键盘的焦点。这是因为将在每个渲染上重新定义
    SampleTestCase
    组件,该组件由状态更改触发。(类似问题:)
  • import React,{useState}来自“React”;
    从“react bootstrap”导入{Form,Button,Col};
    从“反应图标/B”导入{BsPlusSquare};
    const SampleTestCase=({testCase,updateInput})=>{
    返回(
    样本输入
    样本输出
    //setFormObj({
    //…formObj,
    //样本测试案例:{
    //…形成样本测试案例,
    //输出:event.target.value
    //     }
    //   })
    // }
    />
    );
    };
    const AddProblemForm=()=>{
    const[formObj,setFormObj]=useState({
    示例测试用例:[{input:,output:}]
    //其他州obj信息
    });
    const AddSampleTestCase=()=>{
    //创造新的实例!
    const newTestCase={input:,output::};
    setFormObj({
    …formObj,
    样本测试案例:[formObj.sample\u test\u cases,newTestCase]
    });
    };
    控制台日志(formObj);
    const updateInputFor=(i)=>(事件)=>{
    event.preventDefault();
    常数{sample_test_cases}=formObj;
    常量测试用例=样本测试用例[i];
    testCase.input=event.target.value;
    setFormObj({
    …formObj,
    样本测试案例:[
    …样本测试案例。切片(0,i),
    测试用例,
    …样本测试案例。切片(i+1)
    ]
    });
    };
    返回(
    问题表格
    样本测试用例
    {formObj.sample\u test\u cases.map((testCase,i)=>(
    
    ))} 添加示例测试用例 ); }; 导出默认AddProblemForm;
    我建议您在状态复杂的情况下使用useReducer钩子

    当您遇到复杂的问题时,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;
    
    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;
    
    import React,{useReducer}来自“React”;
    从“react bootstrap”导入{Form,Button,Col};
    从“反应图标/B”导入{BsPlusSquare};
    从“/SampleTestCase”导入SampleTestCase;
    常量初始状态={
    样本测试案例:[],
    柜台:0
    //其他州obj信息
    };
    功能减速机(状态、动作){
    开关(动作类型){
    案例“addSampleTestCase”:{
    const{data}=动作;
    返回{
    ……国家,
    样本测试案例:[状态.样本测试案例,数据],
    计数器:state.counter+1
    };
    }
    案例“updateTest”:{
    常量{index,value}=action;
    返回{
    ……国家,
    示例测试用例:state.sample\u test\u cases.map((项目,i)=>{
    如果(i==索引){
    返回值;
    }否则{
    退货项目;
    }
    })
    };
    }
    违约:
    抛出新错误();
    }
    }
    const AddProblemForm=()=>{
    const[state,dispatch]=useReducer(reducer,initialState);
    const AddSampleTestCase=()=>{
    分派({type:“addSampleTestCase”,数据:{input:,output::}});
    };
    /*console.log(状态)*/
    返回(
    问题表格
    样本测试用例
    {state.sample\u test\u cases.map((sample\u test\u case,i)=>(
    分派({type:“updateTest”,索引:i,value})
    }
    />
    
    ))} 添加示例测试用例 ); }; 导出默认AddProblemForm;
    SampleTestCase.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;
    
    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;
    
    从“React”导入React;
    从“react bootstrap”导入{Form,Button,Col};
    const SampleTestCase=({sample\u test\u case,updateValue})=>{
    返回(
    样本输入