[TIL] 내일배움캠프 React 과정 2022.12.13

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랑 친해지고 싶다.