【问题标题】:React UseState is called twice inside eventListenerReact UseState 在 eventListener 中被调用两次
【发布时间】:2020-09-25 21:22:47
【问题描述】:

我开始在空闲时间学习反应。启动 Hooks 时,两次调用 setState 方法。 实时代码在

https://codesandbox.io/s/elastic-saha-mdwwc?file=/src/App.js

添加按钮工作正常。但是当我按下回车键时,setState 函数被调用了两次。最初的想法是重新呈现内容。但我将 useEffect 依赖项作为空数组。所以没有重新渲染完成,我不知道如何调试它。任何帮助表示赞赏:)


const App = () => {
  const [text, setText] = useState('');

  const [list, setList] = useState([]);

  const addToList = () => {
    if (text !== '') {
      setList([...list, text]);
      setText('');
    }
  }

  const deleteItem = index => {
    const deletedList = list.filter((_, i) => i !== index);
    setList(deletedList)
  }
  useEffect(() => {
    console.log("Reloaded");
    const listener = e => {
      if (e.code === 'Enter') {
        console.log("event triggered")
        setText(text => {
          if (text !== '') {
            console.log("updating")
            setList(a => ([...a, text]));
          }
          return ''
        });
      }
    }
    document.getElementById('textbox').addEventListener('keyup', listener);
    console.log('Event registered')
    return () => {
      document.getElementById('textbox').removeEventListener('keyup', listener);
      console.log('Event deregistered')

    }
  }, [])
  return (
    <div >
      <input type="text" id="textbox" onChange={(e) => setText(e.target.value)} value={text} />
      <button id="add" onClick={addToList}> Add</button>
      <ul>
        {
          list.map((a, i) => <li key={i}>{a} <button type="button" onClick={() => deleteItem(i)}>Delete</button></li>)
        }
      </ul>
    </div>
  );
}

export default App;

【问题讨论】:

    标签: reactjs react-hooks use-effect


    【解决方案1】:

    我认为这是因为您代码中的useEffect

    setText(text => {
        if (text !== '') {
            console.log("updating")
            setList(a => ([...a, text]));
        }
        return ''
    });
    

    将其更改为以下内容:

    useEffect(() => {
        console.log("Reloaded");
        const listener = (e) => {
          if (e.code === "Enter") {
            console.log("event triggered");
            if (text !== "") {
              console.log("updating");
              setList((a) => [...a, text]);
            }
            return "";
          }
        };
        document.getElementById("textbox").addEventListener("keydown", listener);
        console.log("Event registered");
        return () => {
          document
            .getElementById("textbox")
            .removeEventListener("keydown", listener);
          console.log("Event deregistered");
        };
      }, [text]);
    

    或者,

    useEffect(() => {
        console.log("Reloaded");
        const listener = (e) => {
          if (e.code === "Enter") {
            console.log("event triggered");
            if (e.target.value !== "") {
              console.log("updating");
              setList((a) => [...a, e.target.value]);
            }
            e.target.value = "";
            return "";
          }
        };
        document.getElementById("textbox").addEventListener("keydown", listener);
        console.log("Event registered");
        return () => {
          document
            .getElementById("textbox")
            .removeEventListener("keydown", listener);
          console.log("Event deregistered");
        };
      });
    

    或者,

    这个使用onKeyPress事件并且只在组件挂载时运行useEffect一次

    import React, { useState, useEffect } from "react";
    
    const App = () => {
      const [text, setText] = useState("");
      const [list, setList] = useState([]);
    
      const addToList = () => {
        if (text !== "") {
          setList([...list, text]);
          setText("");
        }
      };
    
      const deleteItem = (index) => {
        const deletedList = list.filter((_, i) => i !== index);
        setList(deletedList);
      };
    
      const listener = (e) => {
        e.stopPropagation();
        if (e.key === "Enter") {
          console.log("event triggered");
          if (text !== "") {
            console.log("updating");
            setList((a) => [...a, text]);
          }
          setText("");
          return "";
        }
      };
      
      // not needed, only here to show it only runs once
      useEffect(() => {
        console.log("Reloaded");
      }, []);
    
      return (
        <div>
          <input
            type="text"
            id="textbox"
            onChange={(e) => setText(e.target.value)}
            onKeyPress={listener}
            value={text}
          />
          <button id="add" onClick={addToList}>
            {" "}
            Add
          </button>
          <ul>
            {list.map((a, i) => (
              <li key={i}>
                {a}{" "}
                <button type="button" onClick={() => deleteItem(i)}>
                  Delete
                </button>
              </li>
            ))}
          </ul>
        </div>
      );
    };
    
    export default App;
    

    https://codesandbox.io/s/winter-dust-r3ylc?file=/src/App.js

    【讨论】:

    • 我之前试过 useEffect 和依赖。但是我不希望每次在文本框中输入内容时都注册和取消注册我的 keydown 侦听器。据我所知,它们是无关的。除了值之外,文本框节点根本不会被删除/修改。是否可以在没有任何依赖的情况下做到这一点?
    • 感谢@ultimoTG 提供的方法。由于 eslint 警告,我使用 refs 而不是 e.target 和 useCallback,现在它按预期工作。感谢您的时间和精力:)
    • 其实,我只是添加了另一个使用按键事件的解决方案,并且只运行一次useEffect。此外,由于它是 React,因此最好不要直接使用 addEventListenerremoveEventListener 操作 DOM。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-27
    • 2022-06-12
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2022-12-01
    • 2018-05-18
    相关资源
    最近更新 更多