React Context 未按预期工作:无法更改共享变量的值

React Context is not working as expected: Unable to change the value of shared variable

我创建了一个上下文来在我的 nextjs 页面中共享变量“clicked”的值,它似乎没有给出任何错误,但正如您所看到的,即使在单击事件之后变量的值仍然为 FALSE。它不会更改为 TRUE。这是我第一次使用上下文,我做错了什么? 我正在使用打字稿 PS: onClick 事件后,log 数突然增加了 3 或 4,是否被执行了不止一次,但是如何?

controlsContext.tsx

import { createContext, FC, useState } from "react";

export interface MyContext {
    clicked: boolean;
    changeClicked?: () => void;
}

const defaultState = {
  clicked: false,
}

const ControlContext = createContext<MyContext>(defaultState);


export const ControlProvider: FC = ({ children }) => {
    const [clicked, setClicked] =  useState(defaultState.clicked);
    const changeClicked = () => setClicked(!clicked);
  return (
    <ControlContext.Provider
      value={{
          clicked,
          changeClicked,
      }}
    >
      {children}
    </ControlContext.Provider>
  );
};

export default ControlContext;

Model.tsx

import ControlContext from "../contexts/controlsContext";

export default function Model (props:any) { 
    const group = useRef<THREE.Mesh>(null!)
    const {clicked, changeClicked } = useContext(ControlContext);

    const handleClick = (e: MouseEvent) => {
        //e.preventDefault();
        changeClicked();
        console.log(clicked);
    }


    useEffect(() => {
        console.log(clicked);
      }, [clicked]);
    useFrame((state, delta) => (group.current.rotation.y += 0.01));
    const model = useGLTF("/scene.gltf ");
    return (
        <>
        
         <TransformControls enabled={clicked}>
         
        <mesh 
            ref={group} 
            {...props}
            scale={clicked ? 0.5 : 0.2}
            onClick={handleClick}
        >
            <primitive object={model.scene}/>
        </mesh>
        </TransformControls>
        
        </>
    )
}

_app.tsx

import {ControlProvider} from '../contexts/controlsContext';


function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ControlProvider>
      <Component {...pageProps} 
      />
    </ControlProvider>
  )
}

export default MyApp

几件事 -

setClicked((prev) => !prev);

而不是

setClicked(!clicked);

因为它确保它没有使用陈旧状态。那么你也在做 -

changeClicked

但应该是-

changeClicked();

最后,调用设置状态函数后不能直接console.log(clicked),会在下一次渲染时更新

问题

  1. 您实际上并没有调用 changeClicked 回调。
  2. React 状态更新是异步处理的,所以你不能在与排队更新相同的回调范围内记录正在更新的状态,它只会记录当前渲染周期的状态值,而不是它会记录的值在随后的渲染周期中。
  3. 您已将 changeClicked 回调列为可选,因此如果您在调用 changeClicked.
  4. 之前不使用 null-check,Typescript 会警告您

解决方案

const { clicked, changeClicked } = useContext(ControlContext);

...

<mesh 
  ...
  onClick={(event) => {
    changeClicked && changeClicked();
  }}
>
  ...
</mesh>

...

或正常调用时按要求声明changeClicked。您已经提供了 changeClicked 作为默认上下文值的一部分,并且您没有有条件地包含在提供者中,因此没有必要将其设为可选。

export interface MyContext {
  clicked: boolean,
  changeClicked: () => void
}

...

const { clicked, changeClicked } = useContext(ControlContext);

...

<mesh 
  ...
  onClick={(event) => {
    changeClicked();
  }}
>
  ...
</mesh>

...

使用 useEffect 挂钩记录任何状态更新。

const { clicked, changeClicked } = useContext(ControlContext);

useEffect(() => {
  console.log(clicked);
}, [clicked]);

更新

在与您和您的沙箱合作后,它需要进行一些调整。

  1. 使用 ControlProvider 提供程序组件包装 index.tsx JSX 代码,以便向应用程序提供有效的上下文值。这里的 UI 必须重构为一个 React 组件,这样它自己 可以使用上下文值。
  2. HTML canvas 元素或 mesh 元素似乎存在一些问题,导致 Modal 组件无法保持“稳固”连接与反应上下文。目前还不清楚这里的问题是什么,但是将上下文值直接传递给 Modal 组件作为 props 解决了 changeClicked 回调未定义的问题。