I made a context to share the value of the variable "clicked" throughout my nextjs pages, it seems to give no errors but as you can see the variable's value remains FALSE even after the click event. It does not change to TRUE. This is my first time working with context, what am I doing wrong? I'm using typescript PS: After the onClick event the log's number shoots up by 3 or 4, is it being executed more than once, but how?
controlsContext.tsx
import { createContext, useState } from "react";
export interface MyContext {
clicked: boolean,
changeClicked?: () => void
}
export const ControlContext = createContext<MyContext>({
clicked: false,
changeClicked: () => {}
});
export const ControlProvider: React.FC<{}> = (props) => {
const [clicked, setClicked] = useState(false);
const changeClicked = () => setClicked(!clicked);
return (
<ControlContext.Provider
value={{
clicked,
changeClicked
}}
>
{props.children}
</ControlContext.Provider>
);
};
Model.tsx
import { ControlContext } from '../contexts/controlsContext';
export default function Model (props:any) {
const group = useRef<THREE.Mesh>(null!)
const {clicked, changeClicked } = useContext(ControlContext);
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={(event) => {
changeClicked;
console.log(clicked)
}}
>
<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
Please refer this image if needed
CodePudding user response:
A few things -
setClicked((prev) => !prev);
instead of
setClicked(!clicked);
As it ensures it's not using stale state. Then you are also doing -
changeClicked
But it should be -
changeClicked();
Lastly, you cannot console.log(clicked)
straight after calling the set state function, it will be updated in the next render
CodePudding user response:
Issues
- You are not actually invoking the
changeClicked
callback. - React state updates are asynchronously processed, so you can't log the state being updated in the same callback scope as the enqueued update, it will only ever log the state value from the current render cycle, not what it will be in a subsequent render cycle.
- You've listed the
changeClicked
callback as optional, so Typescript will warn you if you don't use a null-check before callingchangeClicked
.
Solution
const { clicked, changeClicked } = useContext(ControlContext);
...
<mesh
...
onClick={(event) => {
changeClicked && changeClicked();
}}
>
...
</mesh>
...
Or declare the changeClicked
as required in call normally. You are already providing changeClicked
as part of the default context value, and you don't conditionally include in in the provider, so there's no need for it to be optional.
export interface MyContext {
clicked: boolean,
changeClicked: () => void
}
...
const { clicked, changeClicked } = useContext(ControlContext);
...
<mesh
...
onClick={(event) => {
changeClicked();
}}
>
...
</mesh>
...
Use an useEffect
hook in to log any state updates.
const { clicked, changeClicked } = useContext(ControlContext);
useEffect(() => {
console.log(clicked);
}, [clicked]);