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?