javascript 为什么这个状态只停留在这个函数中,即使它被改变了?- NextJS 13.3.0 / React

zujrkrfu  于 2023-06-28  发布在  Java
关注(0)|答案(1)|浏览(94)

我有一个按钮来显示使用dialog html元素创建的模态,该按钮存储在一个条件的状态(thingsToRender)中(在本例中,我在useEffect钩子中模拟了该条件),然后如果someCondition为true,则呈现thingsToRender中的内容。问题是checkboxStatehandleRevealButtonClick中似乎总是true,即使在handleCheckboxChange中它的实际值被更改为false后,询问是否不得再次显示模态的复选框被选中。
似乎问题在于将button函数handleRevealButtonClick存储在thingsToRender状态下,但我没有找到一个解决方案来满足我的需求,因为我需要以这种方式呈现按钮,因为在真实的的例子中,这个按钮包含在网格项中。那么,如何才能使该按钮不卡在checkboxState的先前状态中呢?
下面是最小的可重复性示例:

import React, { useEffect, useRef, useState } from 'react'

function Test() 
{
    const [checkboxState, setCheckboxState] = useState(true);
    const [someCondition, setSomeCondition] = useState(false);
    const [thingsToRender, setThingsTorRender] = useState(<h1>Something</h1>);

    const dialogRef = useRef(null);
    const checkboxRef = useRef(null);

    const handleCheckboxChange = () =>
    {
        const checkbox = checkboxRef.current;

        if(checkbox.checked)
        {
            setCheckboxState(false)
        }
        else
        {
            setCheckboxState(true);
        }
    }

    const handleRevealButtonClick = () =>
    {
        if(checkboxState)
        {
            const dialog = dialogRef.current;
            dialog.showModal();
        }
    }

    const handleCloseButtonClick = () =>
    {
        const dialog = dialogRef.current;
        dialog.close();
    }

    useEffect(() =>
    {
        // Problem seems to be here
        setThingsTorRender(<button onClick={handleRevealButtonClick}>Reveal modal</button>)
    }, [])

    return (
        <div>
            <dialog ref={dialogRef}>
                <div>
                    <input type="checkbox" ref={checkboxRef} onChange={handleCheckboxChange}/> 
                    <label>Not reveal again?</label>
                </div>
                
                <button onClick={handleCloseButtonClick}>Close</button>
            </dialog>

            {someCondition === true ? <h1>Some stuff</h1> : thingsToRender}
        </div>)
}

export default Test;

**编辑:**为了阐明为什么按钮需要处于状态,我将展示更接近真实的示例的示例(请注意,thingsToRender包含从firebase文档定义的网格元素):

const renderGrid = () =>
    {
        // query from Firebase
        const grid = query.map((document, index) => 
                <React.Fragment key={index}>
                    <div className='grid-item'>{document.data}</div>
                    <div className='grid-item'>{document.data}</div>
                    <div className='grid-item'>
                        <button onClick={handleRevealButtonClick}>Reveal modal</button>
                    </div>
                </React.Fragment>
            )

        setThingsTorRender(grid)
    }

    useEffect(() =>
    {
        renderGrid();
    }, [])

    return (
        <div>
            {/* dialog */}

            {someCondition === true ? <h1>Some stuff</h1> : <div className='grid-container'> {thingsToRender} </div>}
        </div>)
gzszwxb4

gzszwxb41#

不要在状态下存储JSX(通常没有必要)。相反,将JSX放入return中以避免将其置于state,然后使用state控制query,以便在更改时重新渲染网格:

<div className='grid-container'>{query.map((doc, index) => 
  <React.Fragment key={index}>
    <div className='grid-item'>{doc.data}</div>
    <div className='grid-item'>{doc.data}</div>
    <div className='grid-item'>
      <button onClick={handleRevealButtonClick}>Reveal modal</button>
    </div>
  </React.Fragment>)
}</div>

如果你愿意,你可以把上面的内容放到它自己的Grid组件中,以使它更容易阅读,因为query作为props传递给它。
要理解为什么您当前的方法不起作用,您首先需要弄清楚当您的组件重新呈现时会发生什么。在每次重新渲染时,Test组件将再次执行,创建一个新的作用域,其中存储状态变量、函数等。在Test组件中定义的重新创建。这意味着在组件的不同呈现中存在handleRevealButtonClick回调函数的多个“版本”。handleRevealButtonClick的每个版本只知道创建handleRevealButtonClick的组件的特定渲染的状态值。
您的问题是,您在useEffect()中设置的<button>在组件的初始挂载时创建了一次(由于依赖项数组为空)。这意味着您设置的handleRevealButtonClickonClick回调引用了在第一个渲染中创建的初始handleRevealButtonClick,它只知道组件初始渲染中checkbox状态的值,而不知道它可能被设置为的新值。

相关问题