I'm creating a react game app and I want to pass state across multiple components. For that purpose I'm trying the react context api for the first time. So this is my GameContext.js
import React, { useState, createContext } from 'react';
const GameContext = createContext();
const GameProvider = ({ children }) => {
  const [name, setName] = useState('');
  const [color, setColor] = useState('');
  const [startgame, setStartgame] = useState(false);
  return (
    <GameContext.Provider value={[name, setName]}>
      {children}
    </GameContext.Provider>
  );
};
export { GameContext, GameProvider };
And I'm able to access name in the child component using
import { GameContext } from '../../context/GameContext';    
const [name, setName] = useContext(GameContext);
console.log(name);
But now I want to get the other state values into the same child component, like [color, setColor] and [startgame, setStartgame], from the GameContext.js. 
How do I get those values into a child component? 
I have another question, Somehow I feel this is a really stupid question, but why can't I do something like this in the GameContext.js?...
<GameContext.Provider value={[name, setName,color, setColor, startgame, setStartgame]}>
and get the values in the child component like this...
const [name, setName,color, setColor, startgame, setStartgame] = useContext(GameContext);
I tried this, but the browser is complaining that I'm breaking rules of react hooks.
We can easily pass a single value in the React context. But the question remains on how to pass multiple values in React Context? To pass multiple values in React Context, we can use the Provider API.
If we didn’t use the React Context API, we would have needed to pass the state down to every component as props. In our example, it would have only been a slight annoyance to pass cities and addCity to the right components.
Passing the initial state to React. createContext. This function then returns an object with a Provider and a Consumer. Using the Provider component at the top of the tree and making it accept a prop called value. This value can be anything! Using the Consumer component anywhere below the Provider in the component tree to get a subset of the state.
But the rule of good software development is to always try the simplest solution first. React Context API: Store the state in a Context value in the common ancestor component (called the Provider Component), and access it from as many components as needed (called Consumer Components), which can be nested at any depth under this ancestor.
Provider accepts passing any value so you can paas object here and your values as properties.
<GameContext.Provider
 value={{ name: [name, setName], color: [color, setColor] }}
   >
  {props.children}
</GameContext.Provider>;
and where you are accessing in Child
 const { name, color } = React.useContext(GameContext);
 const [nameValue, setnameValue] = name;
 const [colorValue, setcolorValue] = color;
                        useReducer is better suited to your case:
import React, { useState, useReducer, createContext } from 'react';
const initialState = {
  name: '',
  color: '',
  startgame: false
}
function reducer(state, action) {
  return { ...state, ...action };
}
const GameContext = createContext();
const GameProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <GameContext.Provider value={{ state, dispatch }}>
      {children}
    </GameContext.Provider>
  );
};
export { GameContext, GameProvider };
The child component:
import { GameContext } from '../../context/GameContext'; 
...
const { state: { name, color }, dispatch } = useContext(GameContext);
console.log(name);
console.log(color);
// event handler
const handleChangeName = (event) => {
  dispatch({ name: event.target.value });
}
const handleChangeColor = (event) => {
  dispatch({ color: event.target.value });
}
                        import React, { useState, createContext } from 'react';
const GameContext = createContext();
const GameProvider = ({ children }) => {
  const [state, setState] = useState({
       name: '',
       color: '',
       startgame: false
  });
  return (
    <GameContext.Provider value={{
         ...state, 
         setState: (data) => setState({...state, ...data})
       }}
    >
      {children}
    </GameContext.Provider>
  );
};
export { GameContext, GameProvider };
import { GameContext } from '../../context/GameContext';    
const {name, color, startGame, setState} = useContext(GameContext);
console.log(name);
// Updating name
setState({ name: 'test' });
                        Pass them as an Object like Niyas Nazar commented.
Example:
CommandBarContext.tsx
import React, { createContext, useContext, useState } from 'react'
interface ComandBarState {
  newEnabled: boolean
  setNewEnabled: (state: boolean) => void
  copyEnabled: boolean
  setCopyEnabled: (state: boolean) => void
  deleteEnabled: boolean
  setDeleteEnabled: (state: boolean) => void
}
const commandBarContext = createContext<ComandBarState>({
  newEnabled: true,
  setNewEnabled: (state: boolean) => { },
  copyEnabled: false,
  setCopyEnabled: (state: boolean) => { },
  deleteEnabled: false,
  setDeleteEnabled: (state: boolean) => { },
})
export const CommandBarProvider = (props: any) => {
  const { children } = props
  const [newEnabled, setNewEnabled] = useState<boolean>(true)
  const [copyEnabled, setCopyEnabled] = useState<boolean>(false)
  const [deleteEnabled, setDeleteEnabled] = useState<boolean>(false)
  return (
    <commandBarContext.Provider value={{
      newEnabled, setNewEnabled,
      copyEnabled, setCopyEnabled,
      deleteEnabled, setDeleteEnabled
    }}>
      {children}
    </commandBarContext.Provider>)
}
export const useCommandNew = () => {
  const { newEnabled, setNewEnabled } = useContext(commandBarContext)
  return { newEnabled, setNewEnabled }
}
export const useCommandCopy = () => {
  const { copyEnabled, setCopyEnabled } = useContext(commandBarContext)
  return { copyEnabled, setCopyEnabled }
}
export const useCommandDelete = () => {
  const { deleteEnabled, setDeleteEnabled } = useContext(commandBarContext)
  return { deleteEnabled, setDeleteEnabled }
}
CommandBar.tsx
import React, { FunctionComponent } from 'react'
import {
  CommandBar as FluentCommandBar,
  ICommandBarItemProps,
  ICommandBarStyles
} from '@fluentui/react/lib/CommandBar'
import { Text } from '@fluentui/react/lib/Text'
import { Stack, StackItem } from '@fluentui/react/lib/Stack'
import {
  useCommandNew,
  useCommandCopy,
  useCommandDelete,
} from './CommandBarContext'
import { useTranslation } from 'react-i18next'
export interface CommandBarProps {
  title: string
  onCommandNew?: () => void,
  onCommandCopy?: () => void,
  onCommandDelete?: () => void,
}
export const CommandBar: FunctionComponent<CommandBarProps> = props => {
  const { title } = props
  const {
    onCommandNew = () => { },
    onCommandCopy = () => { },
    onCommandDelete = () => { },
  } = props
  const { newEnabled } = useCommandNew()
  const { copyEnabled } = useCommandCopy()
  const { deleteEnabled } = useCommandDelete()
  const { t } = useTranslation()
  const items: ICommandBarItemProps[] = [
    {
      key: 'commandNew',
      text: t('New'),
      disabled: !newEnabled,
      iconProps: { iconName: 'Add' },
      onClick: onCommandNew,
    },
    {
      key: 'commandCopy',
      text: t('Copy'),
      iconProps: { iconName: 'Copy' },
      disabled: !copyEnabled,
      onClick: onCommandCopy,
    },
    {
      key: 'commandDelete',
      text: t('Delete'),
      iconProps: { iconName: 'Delete' },
      disabled: !deleteEnabled,
      onClick: onCommandDelete,
    },
  ]
  return (
    <Stack horizontal tokens={{ childrenGap: 30 }}>
      <Text variant="xLarge" styles={{ root: { marginTop: 7 } }}>{title}</Text>
      <StackItem grow>
        <FluentCommandBar
          items={items}
          styles={commandBarStyles}
        />
      </StackItem>
    </Stack>
  )
}
const commandBarStyles: ICommandBarStyles = {
  root: {
    marginTop: 0,
    paddingLeft: 0,
    paddingRight: 0
  }
}
Use in parent component:
...
import {
  useCommandNew,
  useCommandCopy,
  useCommandDelete,
} from './CommandBarContext'
...
  const { setNewEnabled } = useCommandNew()
  const { setCopyEnabled } = useCommandCopy()
  const { setDeleteEnabled } = useCommandDelete()
...
  return (
    <CommandBarProvider>
      <Stack tokens={{ childrenGap: 5 }}>
        <CommandBar title="Locations" />
        <Table
          columns={columns}
          items={items}
          onSelection={onSelection}
          onItemInvoked={onItemInvoked}
        />
      </Stack>
      <Panel
        header="Location"
        columns={columns}
        items={selection}
        isPanelOpen={isPanelOpen}
        onCancel={onCancel}
        onSave={onSave}
      />
    </CommandBarProvider>
  )
}
...
                        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