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)
,会在下一次渲染时更新
问题
- 您实际上并没有调用
changeClicked
回调。
- React 状态更新是异步处理的,所以你不能在与排队更新相同的回调范围内记录正在更新的状态,它只会记录当前渲染周期的状态值,而不是它会记录的值在随后的渲染周期中。
- 您已将
changeClicked
回调列为可选,因此如果您在调用 changeClicked
. 之前不使用 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]);
更新
在与您和您的沙箱合作后,它需要进行一些调整。
- 使用
ControlProvider
提供程序组件包装 index.tsx
JSX 代码,以便向应用程序提供有效的上下文值。这里的 UI 必须重构为一个 React 组件,这样它自己 也 可以使用上下文值。
- HTML canvas 元素或
mesh
元素似乎存在一些问题,导致 Modal
组件无法保持“稳固”连接与反应上下文。目前还不清楚这里的问题是什么,但是将上下文值直接传递给 Modal
组件作为 props 解决了 changeClicked
回调未定义的问题。
我创建了一个上下文来在我的 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)
,会在下一次渲染时更新
问题
- 您实际上并没有调用
changeClicked
回调。 - React 状态更新是异步处理的,所以你不能在与排队更新相同的回调范围内记录正在更新的状态,它只会记录当前渲染周期的状态值,而不是它会记录的值在随后的渲染周期中。
- 您已将
changeClicked
回调列为可选,因此如果您在调用changeClicked
. 之前不使用 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]);
更新
在与您和您的沙箱合作后,它需要进行一些调整。
- 使用
ControlProvider
提供程序组件包装index.tsx
JSX 代码,以便向应用程序提供有效的上下文值。这里的 UI 必须重构为一个 React 组件,这样它自己 也 可以使用上下文值。 - HTML canvas 元素或
mesh
元素似乎存在一些问题,导致Modal
组件无法保持“稳固”连接与反应上下文。目前还不清楚这里的问题是什么,但是将上下文值直接传递给Modal
组件作为 props 解决了changeClicked
回调未定义的问题。