【问题标题】:React state not updating in click event with two functions, but updating with one function反应状态不是在点击事件中用两个函数更新,而是用一个函数更新
【发布时间】:2021-09-30 12:49:01
【问题描述】:

这个已经被证明是一段时间以来令人头疼的......

我有一个响应组件,可以更新点击事件的状态。状态是一个简单的布尔值,所以我使用三元运算符来切换状态。

但是,一旦我将第二个函数添加到单击事件状态不再更新,这将起作用。任何想法为什么会发生这种情况以及我做错了什么?

工作代码...

  export default function Activity(props) {
   const [selected, setSelected] = useState(false);

   const selectActivity = () => {
     selected ? setSelected(false) : setSelected(true);
     return null;
   };

   const clickHandler = (e) => {
       e.preventDefault();
       selectActivity();
     };

      return (
        <div
          onClick={(e) => clickHandler(e)}
          className={`visit card unassigned ${selected ? 'selected' : null}`}
        >
          //... some content here
        </div>
      );
     }

状态未更新...

  export default function Activity(props) {
   const [selected, setSelected] = useState(false);

   const selectActivity = () => {
     selected ? setSelected(false) : setSelected(true);
     return null;
   };

   const clickHandler = (e) => {
       e.preventDefault();
       selectActivity();
       props.collectVisitsForShift(
          props.day,
          props.startTime,
          props.endTime,
          props.customer
       );
     };

      return (
        <div
          onClick={(e) => clickHandler(e)}
          className={`visit card unassigned ${selected ? 'selected' : null}`}
        >
          //... some content here
        </div>
      );
     }

【问题讨论】:

  • 这听起来很奇怪,你能试着用setSelected(prev =&gt; !prev)编辑selectActivity吗?
  • 这段代码应该可以正常工作,你确定你没有将密钥传递给Activity,它在运行collectVisitsForShift 时会发生变化?如果是,那么它将重新安装组件并重置状态。

标签: reactjs onclick event-handling use-state


【解决方案1】:

我去散步,发现了这个。我正在从同一个 onClick 事件更改父组件中的状态,这意味着子组件重新渲染并获得其默认状态“false”。

我从父级中删除了状态更改并且它有效。

感谢 Andrei 将我指向 useCallback!

【讨论】:

  • 重新渲染会忽略初始状态,即初始状态的一部分,仅在 first 渲染上设置值。重新安装将为您提供初始状态,因为它是第一次渲染。因此父级可能由于更改了key 或条件渲染而重新挂载Activity
【解决方案2】:

我在 CodeSandbox 环境中加载了您的代码,并且在状态更新方面没有遇到任何问题。但我无权访问您的 collectVisitsForShift 函数,因此无法完全重现您的代码。

但是,您切换状态变量的方式不尊重official guidelines,特别是:

如果下一个状态依赖于当前状态,我们推荐使用更新函数形式

这是我在函数体中得到的结果(在返回 JSX 之前):

const [selected, setSelected] = useState(false);

// - we make use of useCallback so toggleSelected
//   doesn't get re-defined on every re-render.
// - setSelected receives a function that negates the previous value

const toggleSelected = useCallback(() => setSelected(prev => !prev), []);

const clickHandler = (e) => {
    e.preventDefault();
    toggleSelected();
    props.collectVisitsForShift(
        props.day,
        props.startTime,
        props.endTime,
        props.customer
    );
};

useCallback 的文档。

【讨论】:

  • 谢谢!那仍然没有用......我的 collectVisitsForShift 函数从父组件调用并在那里设置状态,如果有帮助的话。 const collectVisitsForShift = (day, start, end, customer) => { const visit = { day, start, end, customer }; setVisitsForShift((v) => [...v, 访问]); };
  • 我明白了。 Activity 的渲染是否以任何方式取决于setVisitsForShift 设置的变量的内容?因为,正如另一条评论所建议的,如果它导致Activity 重新安装,它将重置状态。你可以通过添加以下钩子并检查控制台来调试它:useEffect(() =&gt; console.log("Activity mounted at ", new Date()), []);(它应该只记录一次,当它第一次挂载时。如果它挂载多次,问题出在父组件中)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 2018-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多