score:19

Accepted answer

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.

Edit zk58011yol

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.

score:0

You can still combine them! If you are concerned about performance, you can create the object earlier. I don't know if the values you use change, if they do not it is quite easy:

state = {
  allContextValue: {
    setProfile,
    profileReload,
    deviceTheme,
    setDeviceTheme,
    clickEvent,
    usePopup,
    popup,
    windowSize
  }
}

render() {
  return <AllContext.Provider value={this.state.allContextValue}>...</AllContext>;
}

Whenever you then want to update any of the values you need to do I like this, though:

this.setState({
  allContextValue: {
    ...this.state.allContextValue,
    usePopup: true,
  },
});

This will be both performant, and relatively easy as well :) Splitting those up might speed up a little bit, but I would only do that as soon as you find it is actually slow, and only for parts of your context that would have a lot of consumers.

Still, if your value does not change a lot, there is really nothing to worry about.

score:0

Based on Koushik's answer I made my own typescipt version.

import React from "react"

type TestingContextType = {
  value1?: string,
  value2?: string,
  value3?: string,
  value4?: string,
  value5?: string,
}

const contextDefaultValues = {
  data: { 
    value1: 'testing1',
    value2: 'testing1',
    value3: 'testing1',
    value4: 'testing1',
    value5: 'testing1'
  } as TestingContextType,
  setData: (state: TestingContextType) => {}
};

const TestingContext = React.createContext(contextDefaultValues);

const TestingComponent = () => {
  const {data, setData} = React.useContext(TestingContext);
  const {value1} = data
  return (
    <div>
      {value1} is here
      <button onClick={() => setData({ value1 : 'newline value' })}>
        Change value 1
      </button>
    </div>
  )
}

const App = () => {
  const [data, setData] = React.useState(contextDefaultValues.data)
  const changeValues = (value : TestingContextType) => setData(data && 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>
  )
}

score:2

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 useStates

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>
    )
    }

score:5

This answer already does a good job at explaining how the context can be structured to be more efficient. But the final goal is to make context consumers be updated only when needed. It depends on specific case whether it's preferable to have single or multiple contexts.

At this point the problem is common for most global state React implementations, e.g. Redux. And a common solution is to make consumer components update only when needed with React.PureComponent, React.memo or shouldComponentUpdate hook:

const SomeComponent = memo(({ theme }) => <div>{theme}</div>);

...

<AllContext>
  {({ deviceTheme }) => <SomeComponent theme={deviceTheme}/>
</AllContext>

SomeComponent will be re-rendered only on deviceTheme updates, even if the context or parent component is updated. This may or may not be desirable.


Related Query

More Query from same tag