Split up Context into state and update to improve performance (reduce renders)?
Asked Answered
D

2

3

Some blog articles split up the context into two seperate update and state context when working with React Context. I am wondering whether this really increases the performance or can lead to reduced render cycles.

So the final solution will have:

  • two different context objects
  • two different hooks for 1.) accessing the state and 2.) modifying the state
  • two seperate providers (wrapped into one single one like in AuthProvider)

Solution splitting up Update and State Context:

const authContext = React.createContext<AuthUser | null | undefined>(undefined)
const authUpdateContext = React.createContext<Dispatch<SetStateAction<AuthUser | null>> | null>(null)

export function useAuth() {
    const authUser = React.useContext(authContext);
    if (authUser === undefined) throw new Error(`useAuth must be used within a ContextAuthProvider`);
    return authUser;
}

export const useAuthUpdate = () => {
    const setAuthUser = React.useContext(authUpdateContext);
    if (!setAuthUser) throw new Error(`useAuthUpdate must be used within a AuthProvider`);
    return setAuthuser;
}

export const AuthProvider: React.FC = ({children}) => {
    const [authUser, setAuthUser] = useState<AuthUser | null>(null)
    return (
        <authUpdateContext.Provider value={setAuthUser}>
            <authContext.Provider value={authUser}>
                {children}
            </authContext.Provider>
        </authUpdateContext.Provider>
    )
}

// Usage only in components where needed (one of those two or both combined)
const authUpdate = useAuthUpdate()
const auth = useAuth()
Decrease answered 20/3, 2021 at 1:31 Comment(4)
The actions of the context tend not to change, where as the state those actions control does. If you don't split them, every time the state changes a render will be triggered on all components that subscribe to the context even if they only access the actions which probably haven't changed. Here's a related question: Avoid runnning an effect hook when Context get updatedEmelun
Could you please check the answer?Decrease
The answer seems to miss what you are doing. You can definitely save renders (and related calls such as useEffect) on components that only subscribe to the actions context. The related question I linked clearly shows this. If only one context was used in that example the useEffect in the ActionsComponent would fire on every tick of the counter in the ValueComponent, but it doesn't because each component is subscribing to a different context.Emelun
Thanks, do you want to put that in an answer so other people can quickly see the correct answer?Decrease
E
5

The actions of the context tend not to change, whereas the state those actions control does. If you create a single context that serves both state values and state update methods, every time the state value changes a render will be triggered on all components that subscribe to the context even if they only access the actions (which probably haven't changed).

You can avoid this and save renders (and related side-effects such as useEffect() calls) on components that only subscribe to the actions context by splitting the context into two separate contexts as in your example.

Here is a related question: Avoid runnning an effect hook when Context get updated that clearly illustrates this. If only one context was used in that example the useEffect in the ActionsComponent would fire on every tick of the counter in the ValueComponent, but it doesn't because each component is subscribing to a different context.

And here is a concise rundown of your options in an issue on the React repo: Preventing rerenders with React.memo and useContext hook. #15156: Option 1 (Preferred): Split contexts that don't change together

Emelun answered 20/3, 2021 at 15:10 Comment(1)
yes but splitting the context in two doesn't prevent rerenders of children components, your example, see the log, it prints both "set" and "view". No?Boracic
B
-2

The important thing you need to understand is any components subscribed to a particular context re-renders when the context's value changes. Going back to your question, it doesn't make sense to use a different hook for updating and retrieving the value of the state since both are connected to the context provider. I hope this makes sense. More details here

Ballad answered 20/3, 2021 at 1:43 Comment(1)
The OP is declaring two separate contexts and passing the state value in one, and the state modifiers in the other. These are separate contexts and changing the value on the value context will have no affect on the actions context, thus the components subscribing to the actions context will not have renders triggered on value change.Emelun

© 2022 - 2024 — McMap. All rights reserved.