React 状态不会随 useState 立即更新

React state doesn't update immediately with useState

如果您觉得这是一个重复的问题,我深表歉意,但我在任何类似问题中都找不到解决我的问题的方法,也无法理解为什么会出现此问题。

我有一个名为 Selector.tsx 的组件,它基本上有一个输入字段和一堆按钮,单击这些按钮应该会使用该输入字段中的数据创建一张卡片。

selector.tsx:


const profileList: Profile[] = [
  {
    name: 'Website',
    value: 'website',
  },
  {
    name: 'Documentation',
    value: 'documentation',
  },
  {
    name: 'Telegram',
    value: 'telegram',
  },
  {
    name: 'Twitter',
    value: 'twitter',
  },
  {
    name: 'Github',
    value: 'github',
  },
  {
    name: 'Facebook',
    value: 'facebook',
  },
  {
    name: 'Blog',
    value: 'blog',
  },
  {
    name: 'Discord',
    value: 'discord',
  },
  {
    name: 'LinkedIn',
    value: 'linkedin',
  },
  {
    name: 'Slack',
    value: 'slack',
  },
];

export const Selector: FC<SelectorProps> = ({ wrapperClass }) => {
  const [inputVal, setInputVal] = useState('https://google.com');
  const [saved, setSaved] = useState<Saved[]>([]);

  const handleAdd = (val: string) => {
    if (!inputVal) return;
    const _saved = saved;
    _saved.push({ type: val, text: inputVal });
    setSaved(_saved);
  };

  const handleClearSaved = () => {
    setSaved([]);
  };

  const DisplayCard: FC<DisplayCardProps> = ({ text, type }) => (
    <div className="w-full text-left text-text-gray bg-red-100 px-4 py-1 border rounded-lg">
      <span className="text-gray-600 text-xs">{type}:</span>
      <span className="text-base text-black text-sm ml-4">{text}</span>
    </div>
  );

  return (
    <div className="mt-4 py-4 flex flex-col lg:flex-row-reverse gap-4 items-center">
      <div className="mt-8 space-y-2 overflow-y-auto border border-blue-500 p-4 rounded">
        {saved.length ? (
          saved.map(({ type, text }, i) => (
            <DisplayCard key={i} text={text} type={type} />
          ))
        ) : (
          <i>Nothing to show</i>
        )}
      </div>
      <div className="border rounded p-4">
        <div className="flex items-center gap-2">
          <input
            className="flex-grow pl-2 text-sm leading-6 border border-transparent bg-blue-100 rounded outline-none focus:border-prim-border"
            value={inputVal}
            onChange={(e) => setInputVal(e.target.value)}
          />
          <div className="mt-2 flex flex-col space-y-2">
            {profileList.map(({ name, value }, i) => (
              <button
                className="border border-red-400 text-sm bg-black bg-opacity-0 hover:bg-opacity-10 transform scale-100 active:scale-90 disabled:cursor-not-allowed"
                onClick={() => handleAdd(value)}
                disabled={!inputVal}
              >
                {name}
              </button>
            ))}
          </div>
        </div>
        <button
          className="mt-2 border border-red-500 transform scale-100 active:scale-90 px-2"
          onClick={handleClearSaved}
        >
          Clear Fields
        </button>
      </div>
    </div>
  );
};

每当我们单击任何配置文件按钮(并且输入字段不为空)时,handleSave 按钮将被调用 & saved 数组状态应该得到更新(对象被推入数组)&基于此,DisplayCard被渲染。

问题: 单击任何配置文件按钮后,saved 状态不会更新。当我们在输入字段中进行更改(更新 inputVal 状态)时,只有 saved 状态才会更新

同样奇怪的是,单击清除按钮时 handleClearSaved 功能正常工作。 可以清空saved数组。但是由于某种原因,推入saved数组似乎并没有更新状态.

Stackblitz 中重现的问题:https://stackblitz.com/edit/well-well-well?file=components/Selector.tsx

我无法理解为什么这种行为会在反应中发生。我以前从未遇到过此类问题。 如果有人可以提供帮助或提供任何提示,那将非常有帮助。

谢谢。

使用这个直接改变状态:

const handleAdd = (val: string) => {
  if (!inputVal) return;
  setSaved(prevState => [...prevState, {type: val, text: inputVal}]);
};

此外,您应该在按钮映射中添加一个 key,它会给您一个控制台错误。

原因是您正在使用引用进行变量(对象)复制,当引用未更改时您的状态未更新

代替

 const handleAdd = (val: string) => {
    if (!inputVal) return;
    const _saved = saved;
    _saved.push({ type: val, text: inputVal });
    setSaved(_saved);
  };

试试这个代码

 const handleAdd = (val: string) => {
    if (!inputVal) return;
    setSaved((pre) => ([...pre, { type: val, text: inputVal });
  };

对象和数组通过引用工作,这意味着你不能像这样复制它们

const a = [1,2,3]
const b = a

要复制数组,您应该使用 ES6 Spread 语法来创建这样的新实例

const a = [1,2,3]
cosnt b = [...a]

反应状态检查前一个值和下一个值然后进行更新 当参考没有改变时,不会发生渲染。要更新对象状态或数组状态,您应该创建新实例,并且可以使用 ES6 Spread 语法