【问题标题】:useState hook - state gets lost i.e. resets to initial valueuseState hook - 状态丢失,即重置为初始值
【发布时间】:2020-02-22 13:07:47
【问题描述】:

我正在尝试在安装周期中初始化状态,然后在每次更新时对其进行处理。

但是,状态会以某种方式重置?我不明白为什么。

const [journalItems, setJournalItems] = useState([]);

useEffect(() => {
    fetch(`http://localhost:4000/journals/${props.match.params.key}/items`)
        .then(res => res.json())
        .then(data => {
            setJournalItems(data)    // Sets the state when the AJAX completes
            })
        .catch(err => err);

    table.current = new Tabulator(refTable.current, {
        rowClick: function (e, row) {
            console.log("tabulator journalitems", journalItems) //  State is lost returns []
            handleTableRowClick(row._row.data.id)
        },
        columns: [
            { title: "Компанија", field: "companyName" },
            { title: "Документ", field: "documentKey" },
        ],
    });
}, []);

useEffect(() => {
    console.log("useEffect journalItems", journalItems)    // Works fine
    table.current.replaceData(journalItems)                // Uses the new state
}, [journalItems]);

function handleTableRowClick(journalItemId) {
    console.log("handletablerowclick journalitems", journalItems) // State is lost, resets to []
}

结果来自控制台日志...

useEffect journalItems []
useEffect journalItems (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]

tabulator journalitems []
handleTableRowClick journalitems []

【问题讨论】:

  • 您查看控制台日志的次数是多少?当组件挂载时,带有依赖关系的useEffect 实际上仍然会触发,就像它对[] 所做的那样。因此,我希望看到一个带有空数组的日志,然后如果您的更新成功,则会看到第二个。
  • 是的,我确实看过两次。 1.useEffect journalItems [],2.useEffect journalItems (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]。但是,点击处理程序中的状态为空...
  • 好的,你的更新让问题更清楚了。
  • 我正在阅读possible solution here,但为什么会发生这种情况是没有意义的......
  • @Ivan,也许这可以帮助你stackoverflow.com/questions/54069253/…。状态变化是异步的。

标签: javascript reactjs react-hooks use-effect


【解决方案1】:

我设法用useRef 为函数变量以一种奇怪的方式解决了这个问题,并通过将函数定义移动到useEffect 中?

const [journalItems, setJournalItems] = useState([]);

let handleTableRowClick = useRef(null);

useEffect(() => {
    fetch(`http://localhost:4000/journals/${props.match.params.key}/items`)
        .then(res => res.json())
        .then(data => {
            setJournalItems(data)    // Sets the state when the AJAX completes
            })
        .catch(err => err);

    table.current = new Tabulator(refTable.current, {
        rowClick: function (e, row) {
            console.log("tabulator journalitems", journalItems) //  State is lost returns []
            handleTableRowClick.current(row._row.data.id)
        },
        columns: [
            { title: "Компанија", field: "companyName" },
            { title: "Документ", field: "documentKey" },
        ],
    });
}, []);

useEffect(() => {
    console.log("useEffect journalItems", journalItems)    // Works fine
    table.current.replaceData(journalItems)                // Uses the new state

    handleTableRowClick.current = (journalItemId) => {
        console.log("handletablerowclick journalItems", journalItems)
        // Find the clicked row from all the rows
        let journalItem = journalItems.filter(item => item.id === journalItemId)[0]
        setFormJournalItems(journalItem)
    }
}, [journalItems]);

【讨论】:

    【解决方案2】:

    我相信这是因为journalItems 的初始值在为rowClick 属性定义的函数内被封闭。

    因为包含table.current 的效果只运行一次,所以table.current 值获取所有初始值,并且永远不会使用rowClick 属性的新处理程序进行更新。

    我怀疑您可能需要进行以下更改才能使事情顺利进行:

    const [journalItems, setJournalItems] = useState([]);
    
    useEffect(() => {
        fetch(`http://localhost:4000/journals/${props.match.params.key}/items`)
            .then(res => res.json())
            .then(data => {
                setJournalItems(data)    // Sets the state when the AJAX completes
                })
            .catch(err => err);
    }, [props.match.params.key]);
    
    // Re-create a new `table.current` value (including all handlers) when `journalItems` changes
    table.current = useMemo(() => {
      return new Tabulator(refTable.current, {
            rowClick: function (e, row) {
                console.log("tabulator journalitems", journalItems)
                handleTableRowClick(row._row.data.id)
            },
            columns: [
                { title: "Компанија", field: "companyName" },
                { title: "Документ", field: "documentKey" },
            ],
        });
    }, [journalItems]);
    
    useEffect(() => {
        console.log("useEffect journalItems", journalItems) 
        table.current.replaceData(journalItems)              
    }, [journalItems]);
    
    const handleTableRowClick = useCallback((journalItemId) => {
      console.log("handletablerowclick journalitems", journalItems);
    }, [journalItems]);
    

    不要忘记更新第一个效果的依赖数组。在你的效果中引用的所有来自外部作用域的变量都必须在这个数组中。

    【讨论】:

    • 感谢您的回答。你能检查我的解决方案并发表评论吗?哪种方法更好?
    猜你喜欢
    • 2021-08-08
    • 2019-08-18
    • 1970-01-01
    • 2021-09-22
    • 2020-05-29
    • 1970-01-01
    • 2023-01-27
    • 2020-07-21
    • 2021-09-11
    相关资源
    最近更新 更多