TL;DR use Context
Explanation:
Each component gets its instance of the custom hook.
In the example below, we can see that calling setState
changes the state in the Bla
component but not in the Foo
component:
const { Fragment, useState, useEffect } = React;
const useCustomHook = () => {
const [state, setState] = useState('old');
return [state, setState];
};
const Foo = () => {
const [state] = useCustomHook();
return <div>state in Foo component: {state}</div>;
};
const Bla = () => {
const [state, setState] = useCustomHook();
return (
<div>
<div>state in Bla component: {state}</div>
<button onClick={() => setState("new")}>setState</button>
</div>
);
};
function App() {
return (
<Fragment>
<Foo />
<Bla />
</Fragment>
);
}
ReactDOM.render(<App />, document.querySelector('#root'));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
A combination of Context and custom hook can be used to fix the above issue:
const { createContext, useContext, useState, useEffect } = React;
const Context = createContext();
const ContextProvider = ({ children }) => {
const value = useState("old");
return <Context.Provider value={value} children={children} />;
};
const useCustomHook = () => {
const [state, setState] = useContext(Context);
return [state, setState];
};
const Foo = () => {
const [state] = useCustomHook();
return <div>state in Foo component: {state}</div>;
};
const Bla = () => {
const [state, setState] = useCustomHook();
return (
<div>
<div>state in Bla component: {state}</div>
<button onClick={() => setState("new")}>setState</button>
</div>
);
};
function App() {
return (
<ContextProvider>
<Foo />
<Bla />
</ContextProvider>
);
}
ReactDOM.render(<App />, document.querySelector('#root'));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
Original answer that's specific to what OP wants:
Defining the context:
import React from 'react';
const PlayersContext = React.createContext();
const PlayersProvider = PlayersContext.Provider;
export const usePlayersContext = () => React.useContext(PlayersContext);
export default function PlayersProvider({ children }) {
// add all the logic, side effects here and pass them to value
const { list, loading, error } = useSelector(
(state) => state.accounts.players
);
const dispatch = useDispatch();
useEffect(() => {
try {
const response = authAxios.get(endpoints.players.retrieve);
response.then((res) => {
dispatch({
type: 'SET_PLAYERS_LIST',
payload: {
error: false,
loading: false,
list: res.data.payload,
},
});
});
} catch (e) {
console.log(e);
dispatch({
type: 'SET_PLAYERS_LIST',
payload: {
error: true,
loading: false,
list: [],
},
});
}
}, [dispatch]);
return <PlayersProvider value={{ list, loading, error }}>{children}</PlayersProvider>;
}
Add the PlayersProvider
as a parent to the components that need access to the players.
and inside those child components:
import {usePlayersContext} from 'contextfilepath'
function Child() {
const { list, loading, error } = usePlayersContext()
return ( ... )
}
You can also maintain the state in the Provider
itself instead of in the redux.