在 React 中直接编辑数组中的对象可以工作,但使用钩子则不行

问题描述 投票:0回答:1
import { useState } from "react";

type Rule = {
  value: string;
  valid: boolean;
};

type RuleComponentProps = {
  rule: Rule;
  setValue: (value: string) => void;
  setValid: (valid: boolean) => void;
};

function RuleComponent({ rule, setValue, setValid }: RuleComponentProps) {
  function validator(value: string) {
    rule.valid = !!value;

    // Why below line doesn't work?
    // setValid(!!value);
  }
  function handleChange(e: any) {
    const value = e.target.value;
    validator(value);
    setValue(value);
    // rule.value = value;
  }
  return <input onChange={handleChange} value={rule.value} />;
}

export default function App() {
  const [ruleList, setRuleList] = useState<Rule[]>([]);
  function addRule() {
    setRuleList((prev) => [...prev, { value: "", valid: false }]);
  }
  function getSetValueMeth(index: number) {
    return (value: string) => {
      const newRuleList = ruleList.map((rule, i) => {
        if (index === i) {
          return { ...rule, value };
        }
        return rule;
      });
      setRuleList(newRuleList);
    };
  }

  function getSetValidMeth(index: number) {
    return (valid: boolean) => {
      const newRuleList = ruleList.map((rule, i) => {
        if (index === i) {
          return { ...rule, valid };
        }
        return rule;
      });
      setRuleList(newRuleList);
    };
  }

  return (
    <div className="App">
      <div>
        <button onClick={() => addRule()}>Add rule</button>
      </div>
      {ruleList.map((rule, index) => (
        <p key={index}>
          Rule{index}:{" "}
          <RuleComponent
            rule={rule}
            setValue={getSetValueMeth(index)}
            setValid={getSetValidMeth(index)}
          />
        </p>
      ))}
      <p>{JSON.stringify(ruleList)}</p>
      <button onClick={() => console.log(ruleList)}>Show Rule List</button>
    </div>
  );
}

我正在尝试编辑上面代码中规则列表中的有效值,它在使用

rule.valid = !!value;
时工作,但是当我更改为
setValid(!!value);
方法时,有效值没有改变,即使调用了
setRuleList
钩子并使用正确更新了
newRuleList

我为此创建了一个CodeSandbox:https://codesandbox.io/p/sandbox/react-demo-dfs236?file=%2Fsrc%2FApp.tsx%3A17%2C29,可以直接调试。

我知道在 React 中直接编辑值是不合适的,但为什么钩子不起作用?完成我的功能的正确方法是什么?

reactjs react-hooks
1个回答
0
投票

问题

setValid
(
validator
) 中的 
getSetValidMeth
调用进行的排队状态更新被
setValue
中的
handleChange
调用进行的排队状态更新消除,因为每个都对未更新的状态有一个闭包
ruleList
调用
handleChange
时的状态值。

解决方案

使用功能状态更新,以便每个排队更新正确更新任何先前的状态值,而不是回调范围中关闭的任何值。

const getSetValueMeth = (index: number) => (value: string) =>
  setRuleList((ruleList) =>
    ruleList.map((rule, i) => (index === i ? { ...rule, value } : rule))
  );

const getSetValidMeth = (index: number) => (valid: boolean) =>
  setRuleList((ruleList) =>
    ruleList.map((rule, i) => (index === i ? { ...rule, valid } : rule))
  );

完整代码:

function RuleComponent({ rule, setValue, setValid }: RuleComponentProps) {
  function validator(value: string) {
    setValid(!!value);
  }

  function handleChange(e: any) {
    const value = e.target.value;
    validator(value);
    setValue(value);
  }

  return <input onChange={handleChange} value={rule.value} />;
}

export default function App() {
  const [ruleList, setRuleList] = useState<Rule[]>([]);

  function addRule() {
    setRuleList((prev) => [...prev, { value: "", valid: false }]);
  }

  const getSetValueMeth = (index: number) => (value: string) =>
    setRuleList((ruleList) =>
      ruleList.map((rule, i) => (index === i ? { ...rule, value } : rule))
    );

  const getSetValidMeth = (index: number) => (valid: boolean) =>
    setRuleList((ruleList) =>
      ruleList.map((rule, i) => (index === i ? { ...rule, valid } : rule))
    );

  return (
    <div className="App">
      <div>
        <button onClick={() => addRule()}>Add rule</button>
      </div>
      {ruleList.map((rule, index) => (
        <p key={index}>
          Rule{index}:{" "}
          <RuleComponent
            rule={rule}
            setValue={getSetValueMeth(index)}
            setValid={getSetValidMeth(index)}
          />
        </p>
      ))}
      <p>{JSON.stringify(ruleList)}</p>
      <button onClick={() => console.log(ruleList)}>Show Rule List</button>
    </div>
  );
}
© www.soinside.com 2019 - 2024. All rights reserved.