I am new to React Native and I try to implement a "reset feature" in my game.
And allthough the game data is reset correctly I can’t find out how to rerender the components that are displaying the elements…
Note: some components have been simplified, but they should still reflect the purpose
My gameData
is structured as follows:
{
"puzzles" : [
{ "id": 0, "icon": "0", "codes": ["abc], "unlocked": true },
{ "id": 1, "icon": "1", "codes": ["def"] },
{ "id": 2, "icon": "2", "codes": ["ghi"] }
]
}
so the types for this are
export type GameData = {
puzzles: Puzzle[],
}
export type Puzzle = {
id: number,
icon: string,
codes: string[],
unlocked: boolean,
};
In my game I create a gameContext
that allows unlocking the individual Puzzle
s and (if you want to start over) resetting the state. Therefore I initially load the gameData
from a json and from there on I save the unlocked states in the AsyncStorage
(for a better overview I removed the save & load part – this works fine):
const game = (() => {
// load the initial state from file
const gameData: GameData = require('./gamedata.json');
async function unlockPuzzle(item: Puzzle) {
item.unlocked = true;
// save to AsyncStorage
saveItemState("puzzle", item.id, true);
};
async function loadProgress() {
gameData.puzzles.forEach((item) => {
// load from AsyncStorage
loadItemState("puzzle", item.id).then(unlocked => {
item.unlocked ||= unlocked;
});
});
};
async function resetProgress() {
console.log("reset progress");
gameData.puzzles.forEach((item) => {
item.unlocked = false;
// save to AsyncStorage
saveItemState("puzzle", item.id, item.unlocked);
});
};
return {
gameData,
unlockPuzzle,
loadProgress,
resetProgress,
};
}) ();
export const gameContext = createContext(game);
Now I created a PuzzleCard
component to render the Puzzle
items:
export type PuzzleCardProps = {item: Puzzle} & View['props'];
export function PuzzleCard(props: PuzzleCardProps) {
const game = useContext(gameContext);
const { item, ...otherProps } = props;
const [unlocked, setUnlocked] = useState(item.unlocked);
const handlePress = function() {
game.unlockPuzzle(item);
setUnlocked(true);
};
return <Pressable onPress={handlePress} style={styles.card}>
<Text style={styles.cardIcon}>{item.icon}</Text>
<Text style={styles.cardState}>{unlocked ? "🔓" : "🔒"}</Text>
</Pressable>;
}
and added these items to a list on a tab in my application:
<View style={styles.container}>
{game.gameData.puzzles.map((puzzle: Puzzle, index: number) => {
return <PuzzleCard key={index} item={puzzle}/>
})}
</View>
Everything works fine so far: If the PuzzleCard
is clicked, then
- the matching
Puzzle
item is unlocked - the state is stored to
AsyncStorage
- the
PuzzleCard
is updated (the state changes from "🔓" to "🔒")
Now I want to add a button on a different view in the application to reset the progress, when the player wants to start all over:
export function ResetButton() {
const game = useContext(gameContext);
return <Pressable onPress={ game.resetProgress(); }>Reset</Pressable>
}
Resetting works as intended (in the background)
- all
Puzzle
items are locked - the state is stored to
AsyncStorage
But
- the list of
PuzzleCard
s is NOT updated (state remains "🔒")
If I reload the app / refresh the browser, all items are rerendered and displayed correctly with the new (reset) state.
How can I make sure, that my app rerenders to reflect the changes? (without the need of a reload)
2
Answers
In your context you should use a state to store the data you send, so the updates can be hooked by the components using it.
For example, you can use a state for
gameData
Then update it with the
setGameData
functionThat way, the parts of the template that render the data of
gameData
context will be updated everytimesetGameData
is called.Does this approach suit you ?
It seems like you are not fully understanding the use of
context
within React. YourgameData
is not stateful and therefore will not trigger any re-render of any components relying on it.