Invariant Violation: Changing numColumns on the fly is not supported. Change the key prop on FlatList when changing the number of columns to force a fresh render of the component.
위 에러는 FlatList의 numColumns를 실시간으로 변경할 수 없다는 뜻이다.
이런 경우는 해당 FlatList를 재렌더링을 하는 방식으로 줄 수를 바꿔야 한다.
그렇다면 어떻게 재렌더링을 트리거할 수 있을까?
기기를 회전했을 때 해당 리스트의 key를 바꿔서 다시 받아오는 방법으로 재렌더링이 가능하다.
// min, max : 최소값, 최대값
// exclude: 제외할 정수 (최초 입력값 & 이미 생성한 랜덤값)
const generateNumBetween = ( min: number, max: number, exclude: number): number => {
const randomNum = Math.floor(Math.random() * (max - min)) + min; // 랜덤 정수 생성
//컴퓨터는 첫 시도에서 답을 맞출 수 없음.
if (randomNum === exclude) {
//생성된 숫자가 제외돼야하는 정수와 같다면 다시 생성
return generateNumBetween(min, max, exclude);
} else {
return randomNum;
}
};
매개변수 min, max, exclude는 숫자형이고, 이들을 통해 실행된 함수 generateNumBetween은 숫자형을 반환함을 명시한다.
랜덤 정수를 생성한 방법을 보다 자세히 설명해보자면 아래와 같다.
const randomNum = Math.floor(Math.random() * (max - min)) + min
max - min // 범위 크기 설정 (예: 1부터 100이라면 범위는 99)
Math.random() // 0부터 1사이의 난수 생성
Math.random() * (max - min) // 범위 내의 난수 생성
Math.floor() // 소수점 아래 자리 반올림
Math.floor(Math.random() * (max - min)) // 생성한 랜덤 난수의 소수점 아래 자리 반올림
+ min // min 기준으로 원하는 범위로 이동 (현재는 100 - 1 = 99 -> 0부터 99 이므로, 1부터 100으로 교정)
Math.floor(Math.random() * (max - min)) + min // 완성
이제 게임 화면을 구성한다.
// 타입 선언
interface GameScreenProps {
userNumber: number;
onGameOver: () => void;
}
// 최소값, 최대값 변수 설정
let minBoundary = 1;
let maxBoundary = 100;
export default function GameScreen({ userNumber, onGameOver }: GameScreenProps) {
// 첫번째로 추측할 랜덤 정수
const initialGuess = generateNumBetween(minBoundary, maxBoundary, userNumber);
const [currentGuess, setCurrentGuess] = useState(initialGuess);
useEffect(() => {
if (currentGuess === userNumber) {
onGameOver() // 정답을 맞춘다면 게임오버 화면으로
}
}, [currentGuess, userNumber, onGameOver]);
//유저가 +,-를 올바른 방향으로 사용하도록 유도
const nextGuessHandler = (direction: string) => {
if (
(direction === "lower" && currentGuess < userNumber) ||
(direction === "greater" && currentGuess > userNumber)
) {
Alert.alert("정말인가요?", "다시 한 번 생각해보세요.", [
{ text: "아차차", style: "cancel" },
]);
return; // return을 쓰지 않으면 alert창이 뜨는 동시에 새로운 정수가 생성된다.
}
//수를 올릴 방향. 키울지 줄일지?
if (direction === "lower") {
maxBoundary = currentGuess;
} else {
minBoundary = currentGuess + 1;
}
console.log(minBoundary, maxBoundary); //범위값이 계속 업데이트 되는 것을 확인할 수 있다
// 새로운 랜덤 정수 생성
const newRandomNum = generateNumBetween( minBoundary, maxBoundary, currentGuess );
setCurrentGuess(newRandomNum); //업데이트된 최소값, 최대값, 최근에 썼던 정수를 배제
};
useEffect문의 의존성 배열에 currentGuess, userNumber, onGameOver 가 들어가는 이유?
currentGuess
currentGuess와 userNumber가 일치하는지 여부를 확인하여 게임을 종료해야 한다.
따라서 currentGuess가 바뀔 때마다 if (currentGuess === userNumber) 를 검사한다.
userNumber
userNumber가 바뀐다는 것은 새로운 게임이 시작되었다는 것을 의미하므로, (유저가 게임 도중 바꾸는 경우 포함( 이 로직을 다시 실행해야한다
onGameOver
onGameOver 는 정답 맞췄을 때 실행되는 콜백함수이다.
따라서 onGameOver의 함수 구현이 변경되었다면 useEffect문을 수행해야 한다.