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

Park Taejoon·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탭에서 회색은 렌더링되지 않았음을 의미하고 빗금회색은 기억되었음을 의미하고 주황색은 비교적 오래걸렸다는 것을, 초록색은 빠른 시간안에 렌더링 되었다는 것을 의미한다.

0개의 댓글