React功能组件,useState回调不使用useEffect

React functional component, useState call back without using useEffect

我是 reactJS 的新手,可能遇到了过于复杂的事情或者没有正确计划。

我在使用 useState 挂钩跟踪项目中的属性时遇到了问题。

好的,开始了

    由于使用 react-hook-form
  1. value 取自输入文本框作为文本类型 然后检查
  2. type,看它是否等于“收入”或“支出”
  3. If type == "income" then just set the value using setValue without any changes, else if type == "expense" then *=-1 and set value使用 setValue
  4. parseInt()setValue 一起使用以确保将值保存为数字
  5. 调用
  6. setRecords 将相关信息添加到 record 对象数组中。

所以从上面的 5 个步骤可以看出,useState hook setValuesetRecords 被一个接一个地调用,我现在遇到的问题是应用程序的初始 运行,value 将追加而不是添加。

据我了解,因为 useState 挂钩是异步的,不会立即 return 结果,所以在 setRecords 被调用时, value 仍然是类型文本而不是数字

我已经在网上搜索了一段时间,但没有找到解决这个问题的好方法,没有使用 useEffect 钩子,我尝试使用 useEffect 钩子,但我没能成功,用户可以输入相同的 value 并且应用仍应将该值附加到 record 对象数组中。

最后,我决定新建一个属性 currentValue 并设置在form submit 上,因为这不是状态,会立即更新,不用等待setValue 更新 value 状态。

有没有更好的方法来解决这个问题?或者解决方法可以吗?

import React, { useEffect, useContext } from "react";
import RecordList from "./RecordList";
import { useForm } from "react-hook-form";
import { BudgetPlannerContext } from "../context/BudgetPlannerContext";

const BudgetPlanner = () => {
  const { register, handleSubmit, errors } = useForm();
  const [
    records,
    setRecords,
    total,
    setTotal,
    type,
    setType,
    value,
    setValue,
    description,
    setDescription,
  ] = useContext(BudgetPlannerContext);

  let currentValue = 0;

  useEffect(() => {
    setTotal(() =>
      records.reduce((acc, curr) => {
        return acc + curr.value;
      }, 0)
    );
  }, [records, setTotal]);

  const onFormSubmit = (data) => {
    if ((type === "Income") & (data.value !== 0)) {
      currentValue = parseInt(data.value);
      setValue(parseInt(data.value));
    } else if ((type === "Expense") & (data.value !== 0)) {
      currentValue = parseInt((data.value *= -1));
      setValue(parseInt((data.value *= -1)));
    } else {
      return null;
    }
    setDescription(data.description);
    processValue(type, currentValue);
  };

  const processValue = (type, currentValue) => {
    updateRecord(type, currentValue);
    //setValue(0);
    //setDescription("");
    //console.log(records);
  };

  const updateRecord = (type, currentValue) => {
    setRecords((oldRecord) => [
      ...oldRecord,
      {
        type: type,
        value: currentValue,
        description: description,
        id: Math.random() * 1000,
      },
    ]);
  };

  return (
    <div className="ui segment">
      <div className="search-bar ui segment">
        <form className="ui form" onSubmit={handleSubmit(onFormSubmit)}>
          <div className="field">
            <select
              onChange={(e) => setType(e.target.value)}
              name="todos"
              ref={register}
              className="filter-todo"
            >
              <option value="" defaultValue="defaultValue">
                ---
              </option>
              <option value="Income">Income</option>
              <option value="Expense">Expense</option>
            </select>
            <input
              name="description"
              type="text"
              placeholder="Description"
              ref={register}
            />
            <input
              name="value"
              type="number"
              placeholder="Value"
              ref={register}
            />
          </div>
          <div>Total: {total}</div>
          <button type="submit">Submit</button>
        </form>
      </div>
      <RecordList records={records} setRecords={setRecords} />
    </div>
  );
};

export default BudgetPlanner;

如果我是你,我会将新值作为参数传递给你正在调用的其他函数:

const onFormSubmit = (data) => {
    if (data.value === 0 || (type !== 'Income' && type !== 'Expense')) {
        return;
    }
    const newValue = parseInt(data.value) * (type === 'Income' ? 1 : -1);
    setValue(newValue);
    setDescription(data.description);
    processValue(type, newValue);
};

不需要额外的外部变量。

另一种选择是使用 useEffect(或者,最好是 useLayoutEffect)并在 value 更改时调用 processValue

const onFormSubmit = (data) => {
    if (data.value === 0 || (type !== 'Income' && type !== 'Expense')) {
        return;
    }
    const newValue = parseInt(data.value) * (type === 'Income' ? 1 : -1);
    setValue(newValue);
    setDescription(data.description);
};
useLayoutEffect(() => {
    if (value) {
        processValue(type);
    }
}, [value]);

并让 processValue 使用外部 value 有状态变量。