I am not able to get an updated state from the custom hook.
the example is simple. this is a custom hook allowing to switch from dark to light theme
inside the custom hook, I use useMemo to return a memoized value, but this does not seem to update each time I update the state with switchOn
.
I do not wish to use a context in this example
import { useState, useMemo, useCallback } from "react";
const useTheme = (initial) => {
const [isOn, turnOn] = useState(false);
const switchOn = useCallback((e) => turnOn(e.target.checked), [turnOn]);
return useMemo(() => {
return {
theme: isOn ? "light" : "dark",
buttonTheme: isOn ? "dark" : "light",
lightsIsOn: isOn ? "On" : "Off",
isChecked: isOn,
switchOn,
};
}, [switchOn, isOn]);
};
export default useTheme;
import { useState, useRef, useReducer } from "react";
import useTheme from "./useTheme";
import todos from "./reducer";
import "./App.css";
const Item = ({ id, text, done, edit, remove }) => {
const { buttonTheme } = useTheme();
const isDone = done ? "done" : "";
return (
<div style={{ display: "flex" }}>
<li className={isDone} key={id} onClick={() => edit(id)}>
{text}
</li>
<button type="button" onClick={() => remove(id)} className={buttonTheme}>
<small>x</small>
</button>
</div>
);
};
const SwitchInput = () => {
const { switchOn, isChecked, lightsIsOn } = useTheme();
return (
<>
<label class="switch">
<input type="checkbox" onChange={switchOn} checked={isChecked} />
<span class="slider round"></span>
</label>
<span>
{" "}
Lights <b>{lightsIsOn}</b>
</span>
<br /> <br />
</>
);
};
const initialState = {
items: [],
};
const init = (state) => {
return {
...state,
items: [
{ id: 1, text: "Learning the hooks", done: false },
{ id: 2, text: "Study JS", done: false },
{ id: 3, text: "Buy conference ticket", done: false },
],
};
};
function App() {
const inputRef = useRef();
const [state, dispatch] = useReducer(todos, initialState, init);
const [inputValue, setInputValue] = useState(null);
const { theme } = useTheme();
const handleOnChange = (e) => setInputValue(e.target.value);
const handleOnSubmit = (e) => {
e.preventDefault();
dispatch({ type: "add", payload: inputValue });
inputRef.current.value = null;
};
return (
<div className={`App ${theme}`}>
<SwitchInput />
<form onSubmit={handleOnSubmit}>
<input ref={inputRef} type="text" onChange={handleOnChange} />
<ul>
{state.items.map((item) => (
<Item
{...item}
key={item.id}
remove={(id) => dispatch({ type: "remove", payload: { id } })}
edit={(id) => dispatch({ type: "edit", payload: { id } })}
/>
))}
</ul>
</form>
</div>
);
}
export default App;
- my custom hook: useTheme.js
- below, the component where I want to access logic from custom hook: App.js
I use an App.css to apply theme style : dark and light
below a demo. We can see that the values do not change
How is an effective way to subscribe to state changes share global states between components?
You need to pass the values of
useTheme()
from<App>
to<SwitchInput>
to share it as follows.Then,
<SwitchInput>
will receive them in props.In your example,
useTheme()
is called in both<App>
and<SwitchInput>
, buttheme
and other values are initialized separately and are not shared between each component.