Reputation: 445
I'm trying to switch from using redux in my new projects to using hooks. My understanding is that I can use a custom hook such as this one to an load it from various components to access the same state similar to how redux would let me access state.
import { useState } from 'react';
export const useSelectedStore = () => {
const [selectedStore, setSelectedStore] = useState();
return [
selectedStore,
setSelectedStore,
];
};
The issue that I'm having is that I have a page with an item where when the user clicks an item I need to set the selected store and then redirect them to the page. Here is the click action within the component:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Home from './pages/Home/Home';
import Stores from './pages/Stores/Stores';
import StoreDetails from './pages/StoreDetails/StoreDetails';
function App() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/stores">
<Stores />
</Route>
<Route path="/store-details">
<StoreDetails />
</Route>
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
import React, { useState } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useSelectedStore } from '../../hooks/useSelectedStore';
function Stores(props) {
const { history } = props;
const [selectedStore, setSelectedStore] = useSelectedStore();
const goToStoreDetails = (index) => {
// Now add our store data
setSelectedStore(storeRequestDetails.stores[index]);
console.log(selectedStore);
history.push('/store-details');
};
The console log outputs undefined because the store hasn't been set yet so the history push happens before the value truly gets set.
I tried to use useEffect like this in the component to pick up on the change and handle the change before redirect but the state is getting reset on the component for the store-details page.
import React, { useEffect, useState } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useSelectedStore } from '../../hooks/useSelectedStore';
function Stores(props) {
const { history } = props;
const [selectedStore, setSelectedStore] = useSelectedStore();
useEffect(() => {
if (selectedStore) {
console.log(selectedStore);
history.push('/store-details');
}
}, [selectedStore]);
const goToStoreDetails = (index) => {
// Now add our store data
setSelectedStore(storeRequestDetails.stores[index]);
};
The console log here will show the actual selected store details but again once the next page loads like so the state is now undefined again.
import React from 'react';
import { useSelectedStore } from '../../hooks/useSelectedStore';
function StoreDetails() {
const [selectedStore, setSelectedStore] = useSelectedStore();
console.log(selectedStore); // Returns undefined
How do I share the state correctly between these two components?
Upvotes: 0
Views: 2136
Reputation: 16309
Customs hook do not share any data. They are just compositions of the built-in hooks. But you can still share the store state by using react context:
/* StoreContext.js */
const StoreContext = createContext(null);
export const StoreProvider = ({children}) => {
const storeState = useState();
return (
<StoreContext.Provider value={storeState}>
{children}
</StoreContext.Provider>
);
};
export const useStore = () => useContext(StoreContext);
Usage:
/* App.js */
/* wrap your components using the store in the StoreProvider */
import {StoreProvider} from './StoreContext';
const App = () => (
<StoreProvider>
{/* some children */}
</StoreProvider>
);
and
/* StoreDetails.js */
/* use the useStore hook in your components */
import {useStore} from './StoreContext';
function StoreDetails() {
const [selectedStore, setSelectedStore] = useStore();
// ...
}
Upvotes: 1
Reputation: 84947
My understanding is that I can use a custom hook such as this one to an load it from various components to access the same state similar to how redux would let me access state.
The custom hook you showed will cause multiple components to each create their own independent local states. These sorts of custom hooks can be useful for code reuse, but it doesn't let components share data.
If you want to share data between components, the general react approach is to move the state up the tree until it's at the common ancestor of every component that needs it. Then the data can be passed down either through props or through context. If you want the data to be globally available, then you'll move it to the top of the component tree and almost certainly make use of context rather than props.
This is what redux does: You set up the store near the top of the tree, and then it uses context to make it available to descendants. You can do similar things yourself, by using a similar pattern. Somewhere near the top of the tree (perhaps in App, but you could also move it elsewhere), create state, and share it via context:
export const SelectedStoreContext = React.createContext();
function App() {
const value = useState();
return (
<SelectedStoreContext.Provider value={value}>
<Router>
<div>
//etc
</div>
</Router>
</StoreContext.Provider>
);
}
And to use it:
function StoreDetails() {
const [selectedStore, setSelectedStore] = useContext(SelectedStoreContext);
If you want to call this useSelectedStore and slightly hide the fact that context is involved, you can create a custom hook like this:
const useSelectedStore = () => useContext(SelectedStoreContext);
// used like
const [selectedStore, setSelectedStore] = useSelectedStore();
Upvotes: 4