티스토리 뷰
[React] #8 뽀모도로 타이머 + 투두리스트: 투두리스트 (4) - 랜덤 명언 표시 / 동기와 비동기
무화과(Fig) 2022. 9. 29. 18:06
*피드백은 언제나 환영합니다🙌
오늘은 랜덤 명언 표시 기능을 다뤄보려고 한다.
먼저 QuotesDatabase.js 컴포넌트를 새로 만들었다. 그리고 Today.js 에 import 해주었다.
<div className='todayMainContent'>
<QuotesDatabase />
<p>완료한 작업 개수 : {done}</p>
</div>
그리고 명언과 작가가 담긴 배열을 만들었는데 너무 긴 명언은 뺐다.
const quotes=[
{
"quote": "You can observe a lot just by watching.",
"author": "Yogi Berra"
}, {
"quote": "A house divided against itself cannot stand.",
"author": "Abraham Lincoln"
}, {
"quote": "Difficulties increase the nearer we get to the goal.",
"author": "Johann Wolfgang von Goethe"
}, {
"quote": "Fate is in your hands and no one elses",
"author": "Byron Pulsifer"
},
.
.
.
]
난수 생성하기
- Math.random은 랜덤으로 숫자를 얻고 싶을 때 사용하는 함수다. 하지만 Math.random은 0.0과 같거나 크고 1.0보다 작은 값만을 출력하기 때문에 정수를 출력하고 싶다면 Math.floor(Math.random * 10)을 사용해야 한다.
- Math.floor() 함수는 소수점 1번째 자리를 버림하여 정수를 리턴하는 함수로, 정수인 난수를 생성하기 위한 방법으로 Math.random() 함수와 함께 쓰인다.
따라서 랜덤 quotes를 출력하려면 Math.floor()과 Math.random() 함수를 사용하면 된다.
const getRandomIndex = quotes[Math.floor(Math.random()*quotes.length)];
그리고 명언과 작가를 <span> 태그에 담아서 출력했다.
return (
<div className='quotes'>
<span>{getRandomIndex.quote}</span>
<span>- {getRandomIndex.author}</span>
</div>
)
끝!
인 줄 알았는데
에러떴다
에러 해결 과정: 동기와 비동기
Uncaught TypeError: Cannot read properties of null (reading 'map')
자바스크립트 에러 Top 10 중 첫번째가 떴다. 영광스럽다
먼저 코드를 살펴보자
Today.js
let [todos, setTodos] = useState(()=>
JSON.parse(window.localStorage.getItem("todayInLocal"))
);
useEffect(()=> {
window.localStorage.setItem("todayInLocal", JSON.stringify(todos));
}, [todos]);
.
.
.
return (
<div className='inputButton'>
<TodoInsert onAddTodo={addTodo} />
<TodoList
todos={todos}
setTodos={setTodos}
onRemove={onRemove}
onToggle={onToggle}
addDone={addDone}
removeDone={removeDone}
/>
</div>
)
TodoList.js
function TodoList({todos, setTodos, onRemove, onToggle, addDone, removeDone}) {
return(
<div>
{
todos && todos.map(todo => (
<TodoListItem
{...todo}
key={todo.id}
todos={todos}
setTodos={setTodos}
onRemove={onRemove}
onToggle={onToggle}
addDone={addDone}
removeDone={removeDone}
/>
)
)
}
</div>
)
}
내가 작성한 코드를 설명하자면 다음과 같다.
1. Today.js의 useEffect() 내부에서 localStorage를 이용해 key값으로 todayInLocal을 넣어주었다.
2. 그리고 let [todos , setTodos] = useState()를 이용해 localStorage에서 저장한 값을 쓸 수 있도록 약간의 변경을 했다.
3. return에서 todos를 렌더링해서 화면에 보여준다. 이 과정에서 todos.map이 화면에 반환된다.
에러메세지를 대충 읽어보면 map에서 null값이 나왔다는 얘기같은데.. 저번엔 잘 작동했는데 왜 갑자기 에러가 난거지..? 황당하다.
동기와 비동기
여러가지를 검색하다가 답을 찾았는데 이 오류를 해결하기 위해선 비동기와 동기에 대해 알아야 한다.
동기(Synchronous) 처리 : 서버에 요청을 보냈을 때 응답이 돌아와야 다음 동작을 수행할 수 있다. 따라서 A 작업이 모두 진행될 때 까지 B 작업은 대기해야 한다.
비동기(asynchronous ) 처리: 서버에 요청을 보냈을 때 응답 상태와 상관없이 다음 동작을 수행할 수 있다. 따라서 A 작업이 시작하면 동시에 B 작업이 실행된다. A 작업은 결과값이 나오는대로 출력된다.
리액트로 다음과 같은 state를 만들었다고 해보자.
function App(){
let [name, setName] = useState('kim')
}
setName을 사용하면 name이라는 state를 자유롭게 변경할 수 잇다. 하지만 setName() 같은 state 변경 함수들은 전부 비동기적으로 처리된다. 그래서 setName()을 처리하는데 오래 걸리면 다른 밑에 있는 코드들 부터 실행하기 때문에 문제가 생길 수 있다.
따라서 순차적으로 코드를 실행하고 싶을 때 useEffect를 사용하면 된다. (특정 state가 변경 될 때 useEffect를 실행할 수 있게 짜주면 됨!) 나는 여기까지는 올바르게 한 것 같다. 근데 문제는 useEffect를 이렇게 써도 처음 페이지 로드 될 때도 한 번 실행이 되기 때문에 의도치 않은 버그가 발생할 수 있다고 한다.
그래서 처음 페이지 로드 시 useEffect 실행을 막는 코드를 적용하던가 todos 라는 state를 활용하면 된다.
useEffect(()=> {
window.localStorage.setItem("todayInLocal", JSON.stringify(todos));
}, [todos]);
진짜 코딩애플은 최고다 리액트 강의도 재밌게 들었는데.. 감사합니다 감사합니다...
처음 페이지 로드했을 때 localStorage value 값을 봤더니 전부 null로 표시되어있었다. 이게 문제였군...
그래서 key 가 null 일 때는 (페이지 처음 로드되었을 때는) 코드가 동작하지 않게 조건을 추가했다.
우선 window.localStorage.getItem("todayInLocal")을 saved 변수에 담고 save가 null이 아닐 때 JSON.parse(saved)가 실행되도록 했다. 만약 saved가 null 이라면 빈 배열을 초기값으로 주도록 했다.
빈 배열 값을 준 이유는 null이나 undefined를 제외한 기본값으로 설정해야 자식 요소가 바르게 작동할 수 있기 때문이다.
let [todos, setTodos] = useState(()=> {
const saved = window.localStorage.getItem("todayInLocal");
if(saved !== null) {
return JSON.parse(saved);
} else {
return [];
}
});
이렇게 했더니 다행히 오류가 사라지고 잘 작동됐다!! 동기와 비동기를 확실하게 짚고 넘어가서 오히려 다행이라고 생각한다.
동기, 비동기는 글로만 읽었을 때는 제대로 이해 못하고 감으로 넘겼었는데 이렇게 직접 겪으니까 무엇을 말하는지 알게됐다. 그리고 이런 에러 뜰 때마다 생각하는 건 자바스크립트 공부를 더 해야겠다는 것이다. 강의들을 때는 내가 어떤 부분이 부족한지 몰랐는데.. 여러모로 작은 프로젝트라도 해보는게 도움이 되는 것 같다.
'Toy Project > Time to focus' 카테고리의 다른 글
- Total
- Today
- Yesterday
- 비동기
- 코드잇스프린트
- innerhtml
- 비제어 컴포넌트
- 취업까지달린다
- 프론트엔드
- 스프린트프론트엔드6기
- 코드잇 스프린트
- 리액트
- CSS
- currentTarget
- 동기
- tanstackquery
- map
- Next.js
- html
- 유사배열객체
- javascript
- js
- 중급 프로젝트
- Target
- rest parameter
- hydrationboundary
- Git
- GitHub
- 제어 컴포넌트
- 객체
- 배열
- react
- arguments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |