무료
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결웹 게임을 만들며 배우는 React
Cannot read properties of undefined (reading 'length')
안녕하세요 제로초님, 최대한 질문하지 않고 제힘으로 해결하려했는데 도무지 해결이 안나서 질문드립니다. 처음 시작 할때는 정상으로 작동을 하는데 'start'버튼을 누를경우 이런 에러가 뜹니다. Uncaught TypeError: Cannot read properties of undefined (reading 'length') at Table.jsx:9:24 Table 파일에 length 부분이 이상한거 같아 table파일을 확인해 보니, <table> {Array(tableData.length).fill().map((tr, i) => <Tr rowIndex={i} />)} </table> 이부분에서 오류가 난거 갔습니다. 오타는 없기에 그렇다면 tableData부분이 문제가 있다고 생각이 들었습니다. tableData가 들어있는 파일은 MineSearch.jsx 파일을 샅샅이 뒤졌는데도 오타나 다른부분을 찾지 못하겠습니다.. 제로초님의 github 에서 MineSearch.jsx 파일을 복사 붙여넣기를 해보니 잘 작동합니다. 때문에 MineSearch.jsx 파일부분에 오류가 있는게 확실한데... 도무지 찾기가 쉽지 않습니다. MineSearch.jsx 전체를 붙여놓겠습니다. 의심이 되는 부분만 첨부하라고 하셨는데 거의 모든 부분에 'tableData' 코드를 사용하고 있어서... 일단 첨부합니다.. 너무 시간이 오래걸릴경우 그냥 질문무시 하셔도 됩니다... 항상 너무 감사합니다. import React, { useEffect, useReducer, createContext, useMemo } from 'react'; import Table from './Table'; import Form from './Form'; export const CODE = { MINE: -7, NORMAL: -1, QUESTION: -2, FLAG: -3, QUESTION_MINE: -4, FLAG_MINE: -5, CLICKED_MINE: -6, OPENED: 0, // 0 이상이면 다 opened }; export const TableContext = createContext({ tableData:[], halted:true, dispatch:()=>{} }); const initialState={ tableData:[], timer:0, result:'', halted:true, } const plantMine = (row, cell, mine) => { console.log(row, cell, mine); const candidate = Array(row * cell).fill().map((arr, i) => { return i; }); const shuffle = []; while (candidate.length > row * cell - mine) { const chosen = candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]; shuffle.push(chosen); } const data = []; for (let i = 0; i < row; i++) { const rowData = []; data.push(rowData); for (let j = 0; j < cell; j++) { rowData.push(CODE.NORMAL); } } for (let k = 0; k < shuffle.length; k++) { const ver = Math.floor(shuffle[k] / cell); const hor = shuffle[k] % cell; data[ver][hor] = CODE.MINE; } }; export const START_GAME = 'START_GAME'; export const OPEN_CELL = 'OPEN_CELL'; export const CLICK_MINE = 'CLICK_MINE'; export const FLAG_CELL = 'FLAG_CELL'; export const QUESTION_CELL = 'QUESTION_CELL'; export const NORMALIZE_CELL = 'NORMALIZE_CELL'; export const INCREMENT_TIMER = 'INCREMENT_TIMER'; const reducer = (state, action) =>{ switch(action.type){ case START_GAME: return{ ...state, tableData:plantMine(action.row, action.cell, action.mine), halted:false, } case OPEN_CELL:{ const tableData =[...state.tableData]; tableData[action.row] = [...state.tableData[action.row]]; tableData[action.row][action.cell]=CODE.OPENED; return{ ...state, tableData } } case CLICK_MINE:{ const tableData =[...state.tableData]; tableData[action.row] = [...state.tableData[action.row]]; tableData[action.row][action.cell]=CODE.CLICKED_MINE; return{ ...state, tableData, halted: true, } } case FLAG_CELL:{ const tableData =[...state.tableData]; tableData[action.row] = [...state.tableData[action.row]]; if (tableData[action.row][action.cell] === CODE.MINE){ tableData[action.row][action.cell]=CODE.FLAG_MINE; } else { tableData[action.row][action.cell]=CODE.FLAG; } return{ ...state, tableData, } } case QUESTION_CELL:{ const tableData =[...state.tableData]; tableData[action.row] = [...state.tableData[action.row]]; if (tableData[action.row][action.cell] === CODE.FLAG_MINE){ tableData[action.row][action.cell]=CODE.QUESTION_MINE; } else { tableData[action.row][action.cell]=CODE.QUESTION; } return{ ...state, tableData, } } case NORMALIZE_CELL:{ const tableData =[...state.tableData]; tableData[action.row] = [...state.tableData[action.row]]; if (tableData[action.row][action.cell] === CODE.QUESTION_MINE){ tableData[action.row][action.cell]=CODE.MINE; } else { tableData[action.row][action.cell]=CODE.NORMAL; } return{ ...state, tableData, } } default: return state; } } const MineSearch = () => { const [state, dispatch] = useReducer(reducer, initialState); const { tableData, halted, timer, result } = state; const value = useMemo(() => ({ tableData, halted, dispatch }), [tableData, halted]); return ( <TableContext.Provider value={value}> <Form /> <div>{timer}</div> <Table /> <div>{result}</div> </TableContext.Provider> ); }; export default MineSearch;
- 미해결웹 게임을 만들며 배우는 React
useEffect, useState, 비동기 처리
useEffect(() => { if (isFoucused) { async function load() { try { const rawData: any = await AsyncStorage.getItem('data'); const savedData = JSON.parse(rawData); setData(savedData); } catch (e) { console.log('data load error'); } try { const newObject = data .filter(oneData => oneData.type == 'blank') .map(oneData => ({ key: oneData.key, value: '', })); setBlankDescription(newObject); } catch (e) { console.log('BlankDescription load error'); } } load(); } }, [isFoucused]); 이와 같은 방법으로 비동기를 처리하려 하면 이때 data는 setData가 정의되기 전의 값이 나옵니다. useState 값이 useEffect 내부에 있는 함수가 다 실행이 끝난뒤에 해당 값이 바뀌기 때문인가요? useEffect(() => { if (isFoucused) { async function load() { try { const rawData: any = await AsyncStorage.getItem('data'); const savedData = JSON.parse(rawData); setData(savedData); //이 부분이 문맥상 분리시켜줘야 깔끔할 것 같아서 //해당 부분을 다른 try구문으로 빼려고 했지만, //원활한 비동기 처리를 위해선 savedData를 바로 사용해줘야한다. const newObject = savedData .filter(oneData => oneData.type == 'blank') .map(oneData => ({ key: oneData.key, value: '', })); setBlankDescription(newObject); } catch (e) { console.log('data load error'); } } load(); } }, [isFoucused]); 위 처럼 savedData를 바로 사용해주면 원하는 대로 비동기 처리가 잘 진행 되었습니다. 이렇게 하지 않고 useState값을 변경해준 뒤에 그 useState 값을 사용하면 안 되는 이유 부탁드립니다.. ㅠㅠ
- 미해결웹 게임을 만들며 배우는 React
배포시 Source, React, Redux 자료구조 숨기는 방법?
안녕하세요. 좋은 강의 잘 듣고 있습니다. 강의 내용 중(React Devtools강좌) 배포시 Source, React, Redux 자료구조를 숨겨야 한다고 하셨는데, 어떤식으로 숨기는 건가요? ("다방" 예시 하면서 강의하신 내용 중에서) 복잡한 내용이라면 키워드라도 말씀주시면 찾아보겠습니다.
- 미해결웹 게임을 만들며 배우는 React
핫로딩의 경우 웹스톰과 VSCODE의 차이가 있나요?
안녕하세요 제로초님, 사소한 질문이지만 vscode의 경우 자동 저장 설정이 되어 있으면 데브 서버에서 변경점이 있으면 바로 반영이 되는데 webstorm의 경우엔, Ctrl+S 로 저장을 반드시 눌러줘야 되는 것 같던데 제로초님의 경우 웹스톰 사용하실 때 Ctrl+S를 눌러서 확인하시나요? 궁금해지네요
- 미해결웹 게임을 만들며 배우는 React
안녕하세요 선생님~~
프론트엔드 개발자를 목표로 공부하고 있는 학생입니다 개발 공부를 시작한지 이제 3달정도가 지났습니다 html,css,js공부를 하고 to-do-list를 하나 만들어본 상황입니다 공부는 다 mdn보고 공부를 한 상황이고, js는 es6개념까지 비동기 제외하고는 거의 이해는 한 상태인데 제가 문법 공부만 하고 문제는 거의 풀어본적이 없는데 선생님 강의 듣는데 괜찮을까요?
- 미해결웹 게임을 만들며 배우는 React
함수형 컴포넌트 Hooks 매번 재실행되면?
현영님 안녕하세요. 함수형 컴포넌트가 매번 재실행된다 / 그래서 props, state 등을 쓰지 않을 함수는 밖으로 빼라 라고 하셨는데 그럼 변수선언 useState 할당도 (예를 들면 const [apple, setApple] = useState("사과");) 매번 다시 일어나게 되는건가요? 그건 아닌것 같은데.. 어떻게 이해하면 좋을지 궁금합니다.
- 해결됨웹 게임을 만들며 배우는 React
onSubmitForm이벤트에서 생성된 tries배열관련 질문드립니다!!
안녕하세요 제로초님 강의 정말 잘 듣고있습니다. 해당 강의 16:10초에서 onSubmitForm함수로 스트라이크, 볼을 판단해서 결과를 기존 state의 tries배열을 얕은 복사하여 tries배열에 객체형태로 저장한다고 하셨는데 onSubmitForm함수가 종료되었는데 어떻게 결과들이 tries배열에 누적되어 저장되는 걸까요? 시도한 결과들이 onSubmitForm함수가 종료되어도 어떻게 tries배열에 모두 저장되있는지가 궁금합니다 바쁘시겠지만 답변해주시면 정말 감사하겠습니다!!
- 해결됨웹 게임을 만들며 배우는 React
1:55초에 나오는 react-hot-loader/root 질문드려요
안녕하세요 제로초님 자바스크립트에 이어서 리액트 강의도 정말 잘 듣고 있습니다 감사합니다!! 다름이 아니라 이전 챕터2에서 웹팩과 데브서버를 설치하면서 리액트 기본설정(?)을 학습했는데 본 강의 1:55초에 나오는 코드의 hot부분은 이전 챕터에서 보지못한 부분인데 웹팩, 핫리로딩설정할때 추가로 해줘야하는 부분인가요? 챕터3시작하면서 갑자기 새롭게 생긴 부분이라서 궁금해서 이렇게 질문드립니다. 아래 코드의 hot을 사용하는 부분이 어떤 용도이고 추가를 해야되는지 여부를 알려주시면 감사하겠습니다 ㅜㅜ const { hot } = require('react-hot-loader/root'); const Hot = hot(NumberBaseball); ReactDom.render(<Hot />, .......) 그리고 추가로 챕터2에서 학습한 웹팩, 핫리로딩 설정들은 챕터마다 설정을 해줘야하는걸까요? (구구단 프로젝트폴더에서 설정, 끝말잇기 프로젝트폴더에서 설정......) 아니면 따로 알려주신 npm.... 설치 명령어 없이 그냥 이전 챕터에서 사용한 파일들의 설정을 복사 붙여넣기만 해도 되는걸까요? 바쁘시겠지만 답변해주시면 정말 감사하겠습니다!! (그리고 이런 좋은 강의 무료로 배포해주셔서 감사합니다 ㅎㅎ 무료강의 끝내고 유료강의도 결제해서 꼭 취뽀하겠습니다!!)
- 미해결웹 게임을 만들며 배우는 React
webpack 설정
강사님 강의 좋은 강의 올려주셔서 감사합니다 궁금한게 있는데요 구구단 webpack 따로 야구게임따로 끝말잇기따로 각각 폴더에 webpack 을 설정해야 되는건가요..? 아니면 lecture 폴더하나에 설정해서 공통으로 사용할수 있는건가요..?ㅜㅜ
- 미해결웹 게임을 만들며 배우는 React
winner 메시지 초기화
win 메시지를 테이블이 초기화 될때 <div>{winner}님의 승리</div> 여기 div 내용이 초기화되도록 하려고 하는데요 혹시 all 변수를 true일 때만 보이게 했는데 아래처럼요{winner && <div>{winner}님의 승리</div>} 이걸 {all&& <div>{winner}님의 승리</div>}이렇게 해도 해결이 되지 않아서 질문합니다.
- 미해결웹 게임을 만들며 배우는 React
얕은복사와 깊은 복사에 대해서 질문해요
1. 만약 const num= [a: 1, b: 2] 처럼 주어질 때 const cloneNum = num 이와 같이 그냥 할당하는 경우는 num, cloneNum 둘 다 동일한 주소값을 바라보고 있으므로 (얕은 복사가 아닌)참조관계 라고 하나요? [여태 이런 경우는 얕은 복사로 이해했거든요..그래서 불변성을 지키기 위해 전개 연산자(...) 같은 걸 사용해서 깊은 복사를 해야 한다고 (다른 곳에서)배웠던 거 같아서요.그리고 속성이 기본값이 아닌 참조 타입인 경우1차 깊은 복사만, 내부 속성(2차 깊은 복사)까지 복사되려면 immer같은 라이브러리등을 사용해야 한다고]2. 얕은복사는 최상위 객체만 기존 객체와의 참조 관계를 끊는 (내부는 아직 기존 객체의 주소값을바라보는 ) 걸 말하고 깊은 복사는 내부의 속성들(내부 속성이 객체 또는 배열)까지 참조 관계가 끊어진 상태를 말하나요?3. 그럼 저기 num 배열을const cloneNum2 = [...num]; 처럼 하면 내부 속성은 객체 또는 배열이 아니니 깊은 복사가 되는 건가요? 4. 추가로 값인 경우 예를 들어서let str = 'hello';let cloneStr = str 이렇게 할당하게 되면 기본값은 그냥 깊은 복사가 되는 건가요? 제가 질문을 잘못해서 죄송해요;;얕은복사, 깊은 복사의 의미가 애매해서 질문했어요 ㅜㅜ
- 해결됨웹 게임을 만들며 배우는 React
이번강의 코드의 동작결과를 바탕으로 예측한 동작순서가 맞는지 알고싶습니다.
이번 강의를 보고 코드를 정리한 후 실행결과를 확인했습니다. 콘솔이 생각했던것 보다 7라인 더 찍혀서 궁금해서 질문 남깁니다. 물론 다음 강의에 useMemo나 useCallback으로 문제를 해결하겠지만, 제가 생각한 동작 순서가 맞는지 궁금합니다. 코드 import React, { useEffect, useRef, useState, useMemo } from 'react' import Ball from './Ball' function getWinNumbers() { console.log('getWinNumbers함수') const candidate = Array(45) .fill() .map((v, i) => i + 1) const shuffle = [] while (candidate.length > 0) { shuffle.push( candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0] ) } const bonusNumber = shuffle[shuffle.length - 1] const winNumbers = shuffle.slice(0, 6).sort((p, c) => p - c) return [...winNumbers, bonusNumber] } const Lotto = () => { const [winNumbers, setWinNumbers] = useState(getWinNumbers()) const [winBalls, setWinBalls] = useState([]) const [bonus, setBonus] = useState(null) const [redo, setRedo] = useState(false) const timeouts = useRef([]) useEffect(() => { console.log('useEffect1') for (let i = 0; i < winNumbers.length - 1; i++) { timeouts.current[i] = setTimeout(() => { setWinBalls(prevState => [...prevState, winNumbers[i]]) }, (i + 1) * 1000) } timeouts.current[6] = setTimeout(() => { setBonus(winNumbers[6]) setRedo(true) }, 7000) return () => { timeouts.current.forEach(v => { clearTimeout(v) }) } }, [timeouts.current]) // 빈 배열이면 componentDidMount와 동일 // 배열에 요소가 있으면 componentDidMount랑 componentDidUpdate 둘 다 수행 useEffect(() => { console.log('useEffect2 - 로또 숫자를 생성합니다.') }, [winNumbers]) const onClickRedo = () => { console.log('onClickRedo') setWinNumbers(getWinNumbers()) setWinBalls([]) setBonus(null) setRedo(false) timeouts.current = [] } return ( <> <div>Win Numbers</div> <div id="결과창"> {winBalls.map(v => ( <Ball key={v} number={v} /> ))} </div> <div>Bonus!</div> {bonus && <Ball number={bonus} />} {redo && <button onClick={onClickRedo}>One more</button>} </> ) } export default Lotto 동작 결과 콘솔 11:46:54.502 Lotto.jsx:5 getWinNumbers함수 11:46:54.532 Lotto.jsx:28 useEffect1 11:46:54.532 Lotto.jsx:47 useEffect2 - 로또 숫자를 생성합니다. 11:46:55.951 Lotto.jsx:5 getWinNumbers함수 11:46:56.942 Lotto.jsx:5 getWinNumbers함수 11:46:57.729 Lotto.jsx:5 getWinNumbers함수 11:46:58.536 Lotto.jsx:5 getWinNumbers함수 11:46:59.535 Lotto.jsx:5 getWinNumbers함수 11:47:00.545 Lotto.jsx:5 getWinNumbers함수 11:47:01.536 Lotto.jsx:5 getWinNumbers함수 예측한 동작 순서 1. 함수 컴포넌트에 있는 winNumbers가 getWinNumbers 함수 호출하면서 getWinNumbers함수속 console 출력 -> 'getWinNumbers함수' 2. 27번줄의 useEffect의 두번째 인자인 timeouts.current가 [] 이기 때문에 첫번쨰 useEffect의 console 출력 -> 'useEffect1' 3. 46번줄의 useEffect의 두번째 인자인 winNumbers가 7개의 배열요소가 들어있기 때문에 true 이다 그래서 두번째 useEffect의 console 출력 -> 'useEffect2 - 로또 숫자를 생성합니다.' 4. useEffect1이 출력될때 아래 코드가 6번 동작해서 getWinNumbers함수 속에있는 console 출력 -> 'getWinNumbers함수' x 6 for (let i = 0; i < winNumbers.length - 1; i++) { timeouts.current[i] = setTimeout(() => { setWinBalls(prevState => [...prevState, winNumbers[i]]) }, (i + 1) * 1000) } 그리고 아래 코드가 한번 동작해서 getWinNumbers함수 속에있는 console 출력 -> 'getWinNumbers함수' timeouts.current[6] = setTimeout(() => { setBonus(winNumbers[6]) setRedo(true) }, 7000) 동작 순서 4번에서 (setWinBalls)와 (setBonus + setRedo)가 비동기함수 라서 앞선 콘솔들이 찍히고 마지막에 찍혔던 거죠?
- 미해결웹 게임을 만들며 배우는 React
오류 질문 드립니다...
안녕하세요 강의 듣고 해봤는데 아무리 찾아도 어디서 오류가 난건지 모르겠어서 질문 드립니다 ㅠㅠ 결과값 '땡'은 잘 나타나는데 '정답'일때만 이런 오류가 발생합니다. <html> <head> <meta charset="UTF-8" /> <title>구구단</title> <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> class GuGuDan extends React.Component { constructor(props) { super(props); this.state = { first: Math.ceil(Math.random() * 9), second: Math.ceil(Math.random() * 9), value: '', result: '' }; // 변하는 값 - State } onSubmit = (e) => { e.preventDefault(); if (parseInt(this.state.value) === this.state.first * this.state.second) { this.setSate({ result: '정답', first: Math.ceil(Math.random() * 9), second: Math.ceil(Math.random() * 9), value: '', }); } else { this.setState({ result: '땡', value: '', }); } }; onChange = (e) => { this.setState({value: e.target.value}); }; render() { return ( <div> <div>{this.state.first} 곱하기 {this.state.second}는?</div> <form onSubmit={this.onSubmit}> <input type="number" value={this.state.value} onChange={this.onChange} /> <button>입력!</button> </form> <div>{this.state.result}</div> </div> ); } } </script> <script type="text/babel"> ReactDOM.render(<GuGuDan />, document.querySelector('#root')) </script> </body> </html>
- 해결됨웹 게임을 만들며 배우는 React
useEffect의 두 번째 배열인자에 변하는 값을 넣으면 useEffect안의 코드가 재실행되면서 두번째 배열에 넣은 인자값을 계속 추적하게 되나요?
useEffect(() => { interval.current = setInterval(changeHand, 1000) return () => { clearInterval(interval.current) } }, []) 했을때 가위바위보가 제대로 실행이 안되고 useEffect(() => { interval.current = setInterval(changeHand, 1000) return () => { clearInterval(interval.current) } }, [imgCoord]) 했을때는 가위바위보가 제대로 실행되는 이유가 imgCoord 값이 변경될때 useEffect안에있는 코드들이 다시 실행되어서 인건 이해했습니다. 그렇다면 두 번째 인자 배열에 imgCoord값을 넣으면 useEffect안의 코드인 interval.current = setInterval(changeHand, 1000)가 재실행되어서 imgCoord값이 바뀔때 imgCoord값을 추적하게 되니까 imgCoord값이 '바위' -> '가위' -> '보' -> '바위' 이렇게 바뀔 수 있는 건가요?
- 미해결웹 게임을 만들며 배우는 React
가위바위보 강의에서 질문이 있습니다.
안녕하세요. 제로초님, 가위바위보 강좌에서 2가지 질문이 있습니다. 1. componentDidMount와 WillMount componentDidMount() { this.interval = setInterval(this.computerChange, 50); } componentWillMount() { clearInterval(this.interval); } onClickBtn = (e) => { clearInterval(this.interval); const myChoice = e.target.id; const { imgCoord } = this.state; 콘솔에 찍어보니 WillMount가 먼저 찍히고 그 다음에 DidMount가 찍히는 것으로 확인하였습니다. 제로초님 블로그를 보고 호출되는 순서는 이해를 하였는데요. 위 코드에서 WillMount 함수를 삭제해도 게임에는 아무런 문제가 없는데 렌더링이 되기 전 호출이 되어야 하는, WillMount가 꼭 필요한 상황들이 있을까요? 2. useEffect const onClickBtn = (e) => { if (interval.current) { clearInterval(interval.current); 1번 부분의 hooks 에 제로초님 깃허브 코드에서는 클릭 버튼에 if문이 추가된 것을 보았습니다. 제 생각으로는 useEffect는 콘솔로 확인해보았을 때, 실행이 반복(?)되고 있기때문에 클릭으로 실행되는 onClickBtn 코드는 return문이 실행되기 전이어야 함을 구분하기 위한 것인가…? 라고 생각해보았는데요. 테스트해보니 있어도 없어도 게임 실행에는 문제가 없었는데 깃허브 코드에는 넣으신 이유가 궁금합니다!
- 미해결웹 게임을 만들며 배우는 React
React.memo
강의에서 잠깐 언급된 React.memo에 대해 질문을 할게요 React.memo는 상태(state, props)가 변경될 때에만리랜더링을 시키고 이전 상태와 동일하다면 이전에 저장했던( 메모제이션(?) ) 상태값을 재사용한다고들었는데요 여기서 이미지부분은 변경되는 부분이고 버튼이랑 점수는 버튼을 클릭할 때만 props가 변경되기때문에 버튼을 클릭하지 않을 때에는 이미지부분만하이라이트 표시(개발자 도구에 들어가면 표시되는 부분)가 되어야 되는 거 아닌가요??
- 미해결웹 게임을 만들며 배우는 React
안녕하세요 강사님
안녕하세요. 현영님 강좌 너무 감사하게 잘듣고 이번 강의를 끝냈습니다. 다음 강좌를 nodebird sns로 넘어가려 하는데 문득 궁금한 생각이 들어 질문드립니다. 1.class가 아닌 hooks로 바뀌면서 class로 만들 이유가 많이 사라졌다고 생각합니다. 그럼에도 저와 같은 리액트 입문자 입장에서는 클래스를 통해 훅의 차이를 알기 위해 두 가지 방법을 신경 써서 들었는데요. 앞으로는 훅으로 공부하는 방향을 잡는 게 옳은지 여쭙고 싶습니다. 2. slack 클론 코딩 강좌를 살펴보니 ' 리덕스를 걷어내고, swr을 사용', '자바스크립트-> 타입 스크립트' 이렇게 말씀하셨는데, 1번과 같은 이유로 우선은 기존의 것들을 같이 살펴 가며 nodebird -> slack 강좌 순서로 가는 게 맞는지, 최신 방법들을 먼저 따라가는 게 맞는지 궁금합니다. +개인적으로 여러 인프런 강의 많이 들었는데, 이렇게 세세하고 좋은 강의 없었습니다. 감사합니다.
- 미해결웹 게임을 만들며 배우는 React
memo 질문있습니다!
안녕하세요. 제로초님 리액트 memo는 state와 props가 바뀌지 않을 때 다시 리렌더링을 방지해줌으로써 성능개선이 되는것 같더라구요? 그런데 제로초님의 리액트 노드버드나 sleact강의에서는 memo를 거의 사용하지 않더라구요 혹시 어떨 때 memo를 사용해야 하는 것이 좋고 어떨 때 사용하지 말아야 하는지 궁금합니다!
- 미해결웹 게임을 만들며 배우는 React
질문있습니다!
안녕하세요 제로초님. 3-11강을 수강하던중 의문이 생겼습니다. 1. 만약 try컴포넌트에 memo를 사용하게 되면 숫자를 입력할때는 부모컴포넌트인 NumberBaseball에서 onChangeInput 함수가 실행되어 value state만 변경되어서 자식컴포넌트인 try컴포넌트는 리렌더링이 되자않는것은 이해했습니다. 그러나 숫자를 입력하고 입력버튼을 눌러 onSubmitForm함수가 실행되게 되면 tries state 배열에 데이터가 추가되고 try컴포넌트에 props인 tryInfo props를 넘겨주잖아요? 이때 tries state배열이 변경되었는데 왜 자식컴포넌트인 try를 리렌더링이 되지 않는지 궁금합니다! 입력버튼을 눌렀을 때는 tries의 state가 변하고 props를 넘겨주니 자식컴포넌트인 try 컴포넌트가 한번 렌더링 되어야 하는거 아닌가요?.. 밑에는 전체 코드입니다! const NumberBaseball = () => { const [answer, setAnswer] = useState(getNumbers()); const [value, setValue] = useState(""); const [result, setResult] = useState(""); const [tries, setTries] = useState([]); const inputEl = useRef(null); const onSubmitForm = useCallback( (e) => { e.preventDefault(); if (value === answer.join("")) { setTries((t) => [ ...t, { try: value, result: "홈런!", }, ]); setResult("홈런!"); alert("게임을 다시 실행합니다."); setValue(""); setAnswer(getNumbers()); setTries([]); inputEl.current.focus(); } else { const answerArray = value.split("").map((v) => parseInt(v)); let strike = 0; let ball = 0; if (tries.length >= 9) { setResult(`10번 넘게 틀려서 실패! 답은 ${answer.join(",")}였습니다!`); // state set은 비동기 alert("게임을 다시 시작합니다."); setValue(""); setAnswer(getNumbers()); setTries([]); inputEl.current.focus(); } else { console.log("답은", answer.join("")); for (let i = 0; i < 4; i += 1) { if (answerArray[i] === answer[i]) { console.log("strike", answerArray[i], answer[i]); strike += 1; } else if (answer.includes(answerArray[i])) { console.log( "ball", answerArray[i], answer.indexOf(answerArray[i]) ); ball += 1; } } setTries((t) => [ ...t, { try: value, result: `${strike} 스트라이크, ${ball} 볼입니다.`, }, ]); setValue(""); inputEl.current.focus(); } } }, [value, answer] ); const onChangeInput = useCallback((e) => setValue(e.target.value), []); return ( <> <h1>{result}</h1> <form onSubmit={onSubmitForm}> <input ref={inputEl} maxLength={4} value={value} onChange={onChangeInput} /> <button>입력!</button> </form> <div>시도: {tries.length}</div> <ul> {tries.map((v, i) => ( <Try key={`${i + 1}차 시도 : ${v.try}`} tryInfo={v} /> ))} </ul> </> ); }; export default NumberBaseball; try.js import React, { memo } from 'react'; const Try = memo(({tryInfo}) => { return ( <li> <div>{tryInfo.try}</div> <div>{tryInfo.result}</div> </li> ); }); export default Try;
- 미해결웹 게임을 만들며 배우는 React
안녕하세요 Hook에서 렌더링 질문있습니다.
hook에서 다음과 같은 코드로 렌더링을 몇번 발생하는지 확인해 보았습니다. 첫번째 코드 function App () { const [one, setOne] = useState(0); const [two, setTwo] = useState(0); useEffect(()=>{ setOne(prev => prev + 1); setTwo(prev => prev + 1); }, []); console.log('렌더링!'); return ( <> {one}{two} </> ); } 두번째 코드 function App () { const [one, setOne] = useState(0); const [two, setTwo] = useState(0); useEffect(()=>{ setTimeout(()=>{ setOne(prev => prev + 1); setTwo(prev => prev + 1); }, 1000); }, []); console.log('렌더링!'); return ( <> {one}{two} </> ); 브라우저 콘솔을 확인한 결과, 첫번째 코드는 렌더링이 2번, 두번째 코드는 렌더링이 3번 발생했습니다. 초기 렌더링을 제외하면, 첫번째 코드에서는 react가 setOne과 setTwo를 한번에 처리해줘서 추가적으로 렌더링이 1번 발생한 것 같고, 두번째 코드에서는 react가 setOne과 setTwo를 각각 따로 한번씩 처리해줘서 렌더링이 2번 발생한 것 같은데... 첫번째와 두번째 각각 왜 이러한 현상이 발생하는 건지 궁금합니다..ㅠㅠ