Reputation: 646
I've recently started learning react and i'm using the context api to store my global state.
Here in MyProvider.js file i define my provider and its simply stores 2 arrays of json obj
import {MyContext} from "./MyContext";
import React, {useState} from "react";
export const MyProvider = (props) => {
const intialState = {
featuredListings: [],
setFeaturedListings: (val) => setState({...state, featuredListings: val}),
featuredVendors: [],
setFeaturedVendors: (val) => setState({...state, featuredVendors: val})
}
const [state, setState] = useState(intialState)
return (
<MyContext.Provider
value={state}
>
{props.children}
</MyContext.Provider>
);
}
I'm wrapping all my components in my App.js in the Provider by doing this , side not using ReachRouter to handle routing,
<MyProvider>
<div className="content">
<Header/>
<Router>
<Home path="/"/>
</Router>
</div>
<Footer />
</MyProvider>
In my Home.js file I make a network call in the useEffect hook which successfully returns the json which i expect and with that json response i update the state of the context so that it can be visible globally.
My code for that is
export const Home = () => {
let state = useContext(MyContext)
async function apiCalls() {
const featuredVendors = await getFeaturedVendors()
console.log("featuredVendors - ", featuredVendors) // the correct response is returned
state.setFeaturedVendors(featuredVendors)
const featuredListings = await getFeaturedListing()
console.log("featuredListings - ", featuredListings) // the correct response is returned
state.setFeaturedListings(featuredListings)
}
useEffect(() => {
apiCalls()
}, []);
return (
<div>
{console.log(state.featuredVendors)} // empty array
{console.log(state.featuredListings)} // contains correct values
</div>
)
}
]
To remove any ambiguity my Context is created in a separate file which is Called MyContext.js and I create the Context like so
export const MyContext = React.createContext()
Why is the state.featuredVendors not updating when I set it?
Also another strange thing i noticed is if I rearrange the orders of the calls , i.e call the getFeaturedListing first followed by the getFeaturedVendors then my state only updates for featuredVendors and featuredListings will be an empty array.
Upvotes: 0
Views: 2202
Reputation: 1844
When you call useState
the initialValue is only set once. When your MyProvider is first mounted, the state is initialised with your setFeaturedListings
and setFeaturedVendors
methods but these are not updated whenever the value of state
changes. Therefore the value of state
when you spread the values will always be its initial value.
setState
can also be called with a function that always receives the current value as an argument, so you could rewrite these methods to spread that value like so:
const intialState = {
featuredListings: [],
setFeaturedListings: (val) => setState(state => ({...state, featuredListings: val})),
featuredVendors: [],
setFeaturedVendors: (val) => setState(state => ({...state, featuredVendors: val}))
}
Or, alternatively, you could move these functions outside of your state altogether.
export const MyProvider = (props) => {
const intialState = {
featuredListings: [],
featuredVendors: [],
}
const [state, setState] = useState(intialState)
return (
<MyContext.Provider
value={{
...state,
setFeaturedListings: (val) => setState({...state, featuredListings: val}),
setFeaturedVendors: (val) => setState({...state, featuredVendors: val})
}}
>
{props.children}
</MyContext.Provider>
);
}
Upvotes: 1