Reputation: 876
I am building a snake game with React. Coordinates of the snake (snakeDots
slice of state)are updated every second, and the new state should be reflected in the Snake
component, which renders the snake to the screen. I am using useReduce
so I can have synchronous update of State and I am Using useContext to spread the state to child to components. Upon first render I am getting error for state(snakeDots
) undefined. Don't understand why since the state has initial values. Can anyone shed some light? thanks
In App.js
:
import React, { useState, useEffect, useReducer } from "react";
import Snake from "./Snake";
import Food from "./Food";
import { snakeContext } from "./contexts/snakeContext";
const getRandomCoordinates = () => {
let min = 1;
let max = 98;
let x = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
let y = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
return [x, y];
};
function App() {
const [snakeDots, dispatch] = useReducer(reducer, [
[0, 0],
[2, 0]
]);
const [food, setFood] = useState(getRandomCoordinates());
const [direction, setDirection] = useState("RIGHT");
const [speed, setSpeed] = useState(200);
useEffect(() => {
setInterval(() => dispatch({ type: direction }), speed);
document.onkeydown = onKeyDown;
}, []);
function onKeyDown(e) {
e = e || window.event;
switch (e.keyCode) {
case 38:
setDirection("UP");
break;
case 40:
setDirection("DOWN");
break;
case 37:
setDirection("LEFT");
break;
case 39:
setDirection("RIGHT");
break;
default:
console.log("wrong key");
}
}
function reducer(snakeDots, action) {
switch (action.type) {
case "RIGHT":
let head = snakeDots[snakeDots.length - 1];
head = [head[0] + 2, head[1]];
snakeDots.push(head);
snakeDots.shift();
return snakeDots;
default:
throw new Error();
}
}
return (
<div className="game-area">
<snakeContext.Provider value={{ snakeDots, food }} />
<Snake />
<Food dot={food} />
<snakeContext.Provider />
</div>
);
}
export default App;
in Snake.jsx
:
import React, { useContext } from "react";
import { snakeContext } from "./contexts/snakeContext";
export default function Snake () {
debugger
const { snakeDots } = useContext(snakeContext);
debugger;
return (
<div>
{snakeDots.map((dot, i) => {
const style = {
left: `${dot[0]}%`,
top: `${dot[1]}%`
};
return <div className="snake-dot" key={i} style={style}></div>;
})}
</div>
);
};
in snakeContext.js
:
import { createContext } from "react";
export const snakeContext = createContext();
Edit:
The error message is:
TypeError: Cannot read property 'snakeDots' of undefined
in Snake.js
, the component doesn't even render, line 5.
Upvotes: 2
Views: 1227
Reputation: 12029
I see your issue. You are closing the first provider and not wrapping the snake component, but rather creating TWO providers with no children. Change the return in App.js to:
return (
<div className="game-area">
<snakeContext.Provider value={{ snakeDots, food }}>
<Snake />
<Food dot={food} />
</snakeContext.Provider>
</div>
);
What you currently have (fixed indentation to see the mistake)
return (
<div className="game-area">
{/* First provider */}
<snakeContext.Provider value={{ snakeDots, food }} />
<Snake />
<Food dot={food} />
{/* Second provider */}
<snakeContext.Provider />
</div>
);
Snake is not a child of any Provider, but a sibling.
Upvotes: 1