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