Context API 도입
(1)에서 만들었던 화면에서는 게임 모드를 선택해도 게임판의 격자가 바뀌지 않았다. Gamemode.js
의 state가 Gameboard.js
로 전달되지 않아서인데, state를 공통 상위 컴포넌트로 올려도 되지만 이후 게임판의 좌표를 통해서 드래그&드롭되는 위치를 확인할 계획이기 때문에 컴포넌트 간 여러 변수를 공유할 계획으로 전역변수를 위한 Context API를 사용해 보기로 했다.
context/GameData.js
const GameDataContext = createContext({
state: {
gamemode : 7,
},
actions : {
setGamemode: () => {},
}
})
const GameDataProvider = ({ children }) => {
const [gamemode, setGamemode] = useState(7)
const context = {
state : {
gamemode
},
actions : {
setGamemode
}
}
return (
<GameDataContext.Provider value={context}>
{ children }
</GameDataContext.Provider>
)
}
const { Consumer: GameDataConsumer } = GameDataContext
export { GameDataProvider, GameDataConsumer }
export default GameDataContext
우선은 게임 모드 하나만 전달하기로 하고 초기 상태를 7로 주었다. Context를 사용하는 컴포넌트에서 값을 업데이트하기 위해서 GameDataProvider
함수에서 메소드를 같이 전달해주었다.
App.js
import { GameDataProvider } from './component/context/GameData';
const App = () => {
return (
<div className="top-container">
<GameDataProvider>
<Gamemode/>
<Scoreboard/>
<Gameboard/>
<ObjectList/>
</GameDataProvider>
</div>
)
}
export default App;
Context를 사용할 최상위 컴포넌트(여기서는 App.js
)에서 Provider를 import해준 뒤 하위 컴포넌트들을 감싸주었다.
GameMode.js
import GameDataContext from "./context/GameData"
const Gamemode = () => {
const GameData = useContext(GameDataContext)
const modeList = [5, 7, 9]
const modeChange = (e) => {
GameData.actions.setGamemode(Number(e.target.value))
}
const modeButton = modeList.map((mode) => {
return(
(mode === GameData.state.gamemode) ?
<ModeButton key={mode.toString()} mode={mode} isActive={true} onClick={modeChange}/>
:<ModeButton key={mode.toString()} mode={mode} isActive={false} onClick={modeChange}/>
)
})
return (
<ul>
{ modeButton }
</ul>
)
}
export default React.memo(Gamemode)
Gameboard.js
import GameDataContext from "./context/GameData"
const Gameboard = () => {
const GameData = useContext(GameDataContext)
const makeBlankArray = (size) => {
const blankArray = []
for (let i = 0; i < size; i++) {
blankArray.push([])
for (let j = 0; j < size; j++) {
blankArray[i].push(j)
}
}
return blankArray
}
return (
<div className="top-gameboard-container">
{
makeBlankArray(GameData.state.gamemode).map((horizon, index) => {
return(
<Horizonboard horizon={horizon} line={index} key={"h" + index}/>
)
})
}
</div>
)
}
export default Gameboard
const Horizonboard = ({horizon, line}) => {
return (
<div className="horizon-gameboard">
{
horizon.map((order) => {
return(
<Eachboard order={order} line={line} key={line + String(order)}/>
)
})
}
</div>
)
}
const Eachboard = ({order, line}) => {
return (
<div className="square" id={line + String(order)}>
</div>
)
}
하위 컴포넌트에서는 useContext Hook을 사용하여 접근하였다. 이렇게 하면 Consumer를 사용하지 않아도 돼 보다 간략하게 작성이 가능하다. 다만 여기서 Context를 import하지 않고 useContext만 사용하면 undefined 에러가 발생한다.
GameMode.js
에서는 actions를 통해 각 버튼을 클릭할 때마다 모드를 바꿔주었고, GameBoard.js
에서 크기만큼 빈 행렬을 만들어 출력해주었다.
이제 Context에 게임 스코어나 게임판 좌표 등을 포함하여 전역변수로 이용할 계획이다.