Today I Learned
- React 숙련 개인 과제 시작
Redux로 TO DO LIST 구현하기
시작부터 에러 메시지: could not find react-redux context value
Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider> 라는 에러 메시지가 뜨면서 useSlector 이용해서 state를 불러오는 데 실패하고 있다.
// 추가할 코드
import store from "./redux/config/configStore";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
// App을 Provider로 감싸고, configStore에서 export default한 store를 넣는다.
<Provider store={store}>
<App />
</Provider>
);
문제는 index.js 파일에 있었다! 전혀 생각도 못했던 곳..😨 여기서 Provider와 store를 import해야 한다. 입문 과제 때 작성했던 코드를 토대로 redux로 수정하려다 보니 기초 세팅을 놓쳤던 거 같다.
const todos = useSelector((state) => state);
console.log(todos)
state를 콘솔로 찍어 보면 이렇게 생겼다.
map()으로 투두리스트 제목과 내용 가져오기
캡처한 부분의 컴포넌트 구조가 TodoList > TodoCard > Button이고 원래는 TodoList에서 한 번만 map을 돌려서 각 객체를 props로 전달해주는 방식이었다. 그런데 redux의 목표가 props를 사용하지 않고 Store에서 전역 state를 가져다 쓰는 것이다 보니 각 컴포넌트마다 map을 돌려줘야 하는 건지 고민이 된다.
function TodoList ({isDone}) {
// const { todo, isDone, changeDoneHandler, deleteHandler } = props;
const todos = useSelector((state) => state.todos);
return (
<div className="list">
{/* 제목 변경-isDone이 false면 Working, true면 Done */}
<h2>{ isDone ? "Done..! 🎉" : "Working.. 🔥"}</h2>
<div className="list-container">
{/* isDone 값 true/false에 따라 리스트를 필터링함 */}
{todos.filter((list) => list.isDone === isDone)
.map((list) => {
return (<TodoCard isDone={false} key={list.id} />
);
})}
</div>
</div>
);
};
TodoList 컴포넌트 기존 구조. TodoList 안에 카드 부분만 TodoCard로 들어가 있었다.
function TodoList ({isDone}) {
const todos = useSelector((state) => state.todos.todos);
return (
<div className="list">
{/* 제목 변경-isDone이 false면 Working, true면 Done */}
<h2>{ isDone ? "Done..! 🎉" : "Working.. 🔥"}</h2>
<div className="list-container">
{/* isDone 값 true/false에 따라 리스트를 필터링함 */}
{todos.filter((list) => list.isDone === isDone)
.map((list) => {
return (
<div className="list-container">
<div className="list-card">
<div className="list_text">
<h2 className="todo-title">{list.title}</h2>
<p className="todo-content">{list.content}</p>
</div>
<div className="todo-button">
<button className="deleteBtn">삭제</button>
<Button list={list} key={list.id}/>
</div>
</div>
</div>
);
})}
</div>
</div>
);
};
일단 TodoCard를 TodoList 컴포넌트 안에 넣으면서 TodoCard 컴포넌트를 삭제했다. 그런데 Button 컴포넌트는 취소 버튼인지 완료 버튼인지에 따라서 onClick 함수까지 달라지기 때문에 별개의 컴포넌트로 두는 게 낫겠다는 생각이 들어서 튜터님께 여쭤보니 Button 컴포넌트에서도 불필요하게 map을 돌리는 것보다는 그냥 TodoList에서 map 돌린 각 객체를 Button 컴포넌트에 props로 넘겨주는 게 낫다고 하셔서 원래 방식대로 props로 넘기기로 했다.
파일의 상대 경로 작성하기
폴더 구조가 다음과 같을 때 Form.jsx 파일에 todolist.js 파일의 함수 import 하기. 이게 은근히 어렵다 ㅋㅋ
import { addTodo } from "./../../redux/modules/todolist";
출처 https://velog.io/@mikjk0530/CLI-명령어
Programmers 문제 풀기
OX퀴즈
let number = "3 + 4 = +7"
let a = number.split("")
let b = parseInt(a[0]) + a[2] + parseInt(a[4])
let c = parseInt(a[9])
if (b === c) {
console.log("O")
} else {
console.log("X")
}
console.log(typeof(a[2]))
console.log(b)
console.log(c)
처음에는 이렇게 접근했었다.
let number = "3 - 4 = -1"
let a = number.split(" ");
let c;
for (const x of a) {
if (!isNaN(x)) {
c += parseInt(x)
// b.push(parseInt(x))
console.log(c)
} else {
c += x
}
}
split 기준을 ""에서 " "로 바꾸니(띄어쓰기 추가) 딱 내가 원하는 대로 배열이 나눠진다. 근데 자꾸 배열의 첫 번째 숫자만 NaN이 뜨는데 이유를 모르겠다. 원래는 변수 c를 선언만 하고 할당을 하지 않은 이유는 만약에 let c = ""라고 하면 결과적으로 또 문자열이 되어 버리기 때문이었는데 let c = 0;으로 할당하는 방법도 있었다. c에 0을 할당하니까 NaN이 뜨던 문제가 해결되었다.
회고
과제 시작하려고 하는데 리덕스 이용해서는 기존의 데이터를 어떻게 수정할 수 있는지 흐름을 잘 모르겠다. setState를 이용하면 되는 걸까? 리덕스 너무 어려운 거 같다. 약간 기억이 미화되었을 수도 있겠지만 firebase랑 spa 공부하던 때 보다 더 어려운 느낌 ㅠㅠ 빨리 redux랑 친해지고 싶다.