Redux #8 Redux로 Todo만들어보기 + 최적화

TaejoonPark·2022년 4월 9일
0

Redux

목록 보기
8/9

혼자 안보고 해보니까 리덕스 사용방법을 더 잘 알 것 같다. 글로 정리해놓을 때는 탑다운 순서로 써놓을 것이나 직접 코드를 쓸때는 바텀업 방식으로 했다.

base

index.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./modules";
import {composeWithDevTools} from "redux-devtools-extension";

const store = createStore(reducers, composeWithDevTools())

ReactDOM.render(
  <React.StrictMode>
      <Provider store={store}>
        <App />
      </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

App.jsx

import React from 'react';
import TodosContainer from "./containers/TodosContainer";

function App() {
  return (
    <TodosContainer />
  );
}

export default App;

modules

modules/index.jsx

import {combineReducers} from "redux";
import todos from './todos';

const reducers = combineReducers({todos});
export default reducers;

modules/todos.jsx

// 액션 상태
const ADD_TODO = 'modules/ADD_TODO';
const TOGGLE_TODO = 'modules/TOGGLE_TODO';

const initialState = [
    /*
    {
        id: 1,
        text: '',
        done: false,
    }
     */
]

// 액션 생성함수
export const addTodo = text => ({
    type: ADD_TODO,
    text
})

export const toggleTodo = id => ({
    type: TOGGLE_TODO,
    id
})

let nextId = 1;
// 리듀서
export default function todos (state = initialState, action) {
    switch(action.type) {
        case ADD_TODO:
            return [
                ...state,
                {id: nextId++, text: action.text, done: false}
            ]
        case TOGGLE_TODO:
            return state.map(state => state.id === action.id ? {...state, done: !state.done} : state);
        default:
            return state;
    }
}

Containers

TodosContainer.jsx

import React from 'react';
import {useSelector, useDispatch} from "react-redux";
import Todos from "../components/Todos";
import {addTodo, toggleTodo} from "../modules/todos";


export default function TodosContainer() {
    const todos = useSelector(state => state.todos);
    const dispatch = useDispatch();
    const onCreate = (text) => {
        dispatch(addTodo(text));
    }
    const onToggle = (id) => {
        dispatch(toggleTodo(id))
    }

    return <Todos onCreate={onCreate} onToggle={onToggle} todos={todos}/>
}

Components

구조

Todo
┠ TodoInput
┗ TodoList
  ┗ Todo

Todos.jsx

import React, {useState} from 'react';
import TodoInput from "./TodoInput";
import TodoList from "./TodoList";

export default function Todos({todos, onCreate, onToggle}) {
    const [text, setText] = useState('');
    const onSetText = (text) => {
        setText(text);
    }

    return (
        <>
            <TodoInput text={text} onSetText={onSetText} onCreate={onCreate}/>
            <TodoList todos={todos} onToggle={onToggle}/>
        </>
    )
}

TodoInput.jsx

import React from 'react';

export default function TodoInput({text, onSetText, onCreate}) {
    const onSubmit = (e) => {
        e.preventDefault();
        onCreate(text);
    }
    const onChange = (e) => {
        onSetText(e.target.value);
    }

    return (
        <form onSubmit={onSubmit}>
            <input value={text} onChange={onChange} />
            <button type={"submit"}>추가하기</button>
        </form>
    )
}

TodoList.jsx

import React from 'react';
import Todo from "./Todo";

export default function TodoList({todos, onToggle}) {
    return (
        <ul>
            {todos.map(todo => <Todo todo={todo} onToggle={onToggle}/>)}
        </ul>
    )
}

Todo.jsx

import React from 'react';
import './Todo.scss'

export default function Todo({todo, onToggle}) {
    const onClick = () => {
        onToggle(todo.id);
    }
    return <li className={todo.done? 'complete' : null} onClick={onClick}>{todo.text}</li>
}

최적화 해보기

todo에 추가하기 위해 input에 쓰게 되면 리스트 전체가 다시 리렌더링되는 것을 확인할 수 있다. input에 작성할 동안은 리스트가 변하지 않기 때문에 현 상태를 메모이제이션해둘 수 있다.
위 예제에서 TodoItem, TodoList 이런 컴포넌트들을 하나씩 React.memo(component) 식으로 감싸주면서 확인해보면 리렌더링을 하지않는 것을 확인할 수 있다.

크롬브라우저에서 React관련 extension을 설치하고 Profiler탭에서 녹화버튼을 누르고 확인하는 방법도 있고 Components탭에서 렌더될 때 highlight되는 옵션을 체크해도 된다. Profiler탭에서 회색은 렌더링되지 않았음을 의미하고 빗금회색은 기억되었음을 의미하고 주황색은 비교적 오래걸렸다는 것을, 초록색은 빠른 시간안에 렌더링 되었다는 것을 의미한다.

profile
공유하는 것을 좋아하는 프론트엔드 개발자

0개의 댓글

관련 채용 정보