What is the best way to share some global values and functions in react?
Now i have one ContextProvider with all of them inside:
<AllContext.Provider
value={{
setProfile, // second function that changes profile object using useState to false or updated value
profileReload, // function that triggers fetch profile object from server
deviceTheme, // object
setDeviceTheme, // second function that changes theme object using useState to false or updated value
clickEvent, // click event
usePopup, // second function of useState that trigers some popup
popup, // Just pass this to usePopup component
windowSize, // manyUpdates on resize (like 30 a sec, but maybe can debounce)
windowScroll // manyUpdates on resize (like 30 a sec, but maybe can debounce)
}}
>
But like sad in docs: Because context uses reference identity to determine when to re-render, there are some gotchas that could trigger unintentional renders in consumers when a provider’s parent re-renders. For example, the code below will re-render all consumers every time the Provider re-renders because a new object is always created for value:
This is bad:
<Provider value={{something: 'something'}}>
This is ok:
this.state = {
value: {something: 'something'},
};
<Provider value={this.state.value}>
I imagine that in future i will have maybe up to 30 context providers and it's not very friendly :/
So how can i pass this global values and functions to components? I can just
Examples of what i use in Provider:
// Resize
const [windowSize, windowSizeSet] = useState({
innerWidth: window.innerWidth,
innerHeight: window.innerHeight
})
// profileReload
const profileReload = async () => {
let profileData = await fetch('/profile')
profileData = await profileData.json()
if (profileData.error)
return usePopup({ type: 'error', message: profileData.error })
if (localStorage.getItem('deviceTheme')) {
setDeviceTheme(JSON.parse(localStorage.getItem('deviceTheme')))
} else if (profileData.theme) {
setDeviceTheme(JSON.parse(JSON.stringify(profileData.theme)))
} else {
setDeviceTheme(settings.defaultTheme)
}
setProfile(profileData)
}
// Click event for menu close if clicked outside somewhere and other
const [clickEvent, setClickEvent] = useState(false)
const handleClick = event => {
setClickEvent(event)
}
// Or in some component user can change theme just like that
setDeviceTheme({color: red})
The main consideration (from a performance standpoint) for what to group together is less about which ones are used together and more about which ones change together. For things that are mostly set into context once (or at least very infrequently), you can probably keep them all together without any issue. But if there are some things mixed in that change much more frequently, it may be worth separating them out.
For instance, I would expect deviceTheme
to be fairly static for a given user and probably used by a large number of components. I would guess that popup
might be managing something about whether you currently have a popup window open, so it probably changes with every action related to opening/closing popups. If popup
and deviceTheme
are bundled in the same context, then every time popup
changes it will cause all the components dependent on deviceTheme
to also re-render. So I would probably have a separate PopupContext
. windowSize
and windowScroll
would likely have similar issues. What exact approach to use gets deeper into opinion-land, but you could have an AppContext
for the infrequently changing pieces and then more specific contexts for things that change more often.
The following CodeSandbox provides a demonstration of the interaction between useState and useContext with context divided a few different ways and some buttons to update the state that is held in context.
You can go to this URL to view the result in a full browser window. I encourage you to first get a handle for how the result works and then look at the code and experiment with it if there are other scenarios you want to understand.
The answer by Ryan is fantastic and you should consider that while designing how to structure the context provider hierarchy.
I've come up with a solution which you can use to update multiple values in provider with having many useState
s
Example :
const TestingContext = createContext()
const TestingComponent = () => {
const {data, setData} = useContext(TestingContext)
const {value1} = data
return (
<div>
{value1} is here
<button onClick={() => setData('value1', 'newline value')}>
Change value 1
</button>
</div>
)
}
const App = () => {
const values = {
value1: 'testing1',
value2: 'testing1',
value3: 'testing1',
value4: 'testing1',
value5: 'testing1',
}
const [data, setData] = useState(values)
const changeValues = (property, value) => {
setData({
...data,
[property]: value
})
}
return (
<TestingContext.Provider value={{data, setData: changeValues}}>
<TestingComponent/>
{/* more components here which want to have access to these values and want to change them*/}
</TestingContext.Provider>
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With