I'm trying to reuse a bunch of custom hooks without re-invoking them and without maintaining an order through which I'll have to pass cascading parameters from one hook to the other.
A working example: https://codesandbox.io/s/laughing-firefly-mlhdw?file=/src/App.js:0-1158
Given the following code:
import React, { useContext, useEffect, useState } from "react";
const globalContext = React.createContext({
user: null,
pet: null
});
const usePet = () => {
const [pet, setPet] = useState(null);
useEffect(() => {
setTimeout(() => {
setPet("Dog");
}, 3000);
}, []);
return pet;
};
const useUser = () => {
const [user, setUser] = useState(null);
// I want to proxy pet via the context so that I won't have to re-invoke its side-effects again
const { pet } = useContext(globalContext);
useEffect(() => {
setTimeout(() => {
setUser("john");
}, 500);
}, []);
// This is only called once with the default value (null)
useEffect(() => {
console.log("Called from user!", { pet });
}, [pet]);
return user;
};
export const StateProvider = ({ children }) => {
const user = useUser();
const pet = usePet();
useEffect(() => {
console.log("StateProvider", { user, pet });
}, [user, pet]);
return (
<globalContext.Provider value={{ user, pet }}>
{children}
</globalContext.Provider>
);
};
export default function App() {
const { user, pet } = useContext(globalContext);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>
{pet} {user}
</h2>
</div>
);
}
// imagine an index.js that's wrapping the App component like this:
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<StateProvider>
<App />
</StateProvider>
</StrictMode>,
rootElement
);
What I'm expecting to see in the console output is the following
Called from user! {pet: null}
StateProvider {user: null, pet: null}
StateProvider {user: "john", pet: null}
StateProvider {user: "john", pet: "Dog"}
Called from user! {pet: "Dog"}
But I'm not getting any updates inside useUser
other than the initial state:
Called from user! {pet: null}
StateProvider {user: null, pet: null}
StateProvider {user: "john", pet: null}
StateProvider {user: "john", pet: "Dog"}
<!-- no update here, pet is still null for the useUser hook -->
My questions are:
- Is it possible to achieve that? if it is, what am I missing here?
- If it's not possible, is there a more elegant way of passing data between custom hooks without re-invoking them (creating a new state context for each invocation) and without passing parameters from one another, which will force me to also maintain order between everything?
Just to clarify - the UI is working as expected and all the values are rendered correctly inside the component.
Also, when passing the parameters directly to the hook, things are also in order
const pet = usePet();
const user = useUser(pet); //this will work as it doesn't go through the context