ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React관련 싹 훑기(6) - react-redux hooks 부분 문서 읽고 실습
    Web Dev/3. React 관련 2021. 5. 26. 14:14
    728x90

    Hooks 파트 문서 읽기

    https://react-redux.js.org/api/hooks

     

    Hooks | React Redux

    Hooks

    react-redux.js.org

    공식문서 예시를 내가 주로 쓰는 함수형과 hooks만쓰도록 한번 만들어 보려고 한다. 

     

    1. create-react-app 실행 후에 redux, react-redux 설치

    - redux installation guide: https://redux.js.org/introduction/installation 

    - react-redux installation guide: https://react-redux.js.org/introduction/getting-started

    yarn add redux react-redux
    yarn add -D redux-devtools
    

     

     

    2. Provider API를 통해서 Redux Store를 react component에서 쓸수 있도록 한다. Redux Store에는 reducer를 넘겨주어야한다. [redux api 참고]

    파일구조, redux폴더 내에 reducer와 store를 정의한다

    // store.js 파일 
    import { createStore } from "redux";
    import rootReducer from "./reducers";
    
    export default createStore(rootReducer);
    
    
    // reducers/index.js
    // reducer를 comine해준다
    import { combineReducers } from "redux";
    import visibilityFilter from "./visibilityFilter";
    import todos from "./todos";
    
    export default combineReducers({ todos, visibilityFilter });

     

    3. redux devtools 설정

    https://github.com/zalmoxisus/redux-devtools-extension#installation

     

    zalmoxisus/redux-devtools-extension

    Redux DevTools extension. Contribute to zalmoxisus/redux-devtools-extension development by creating an account on GitHub.

    github.com

    // store.js
    import { createStore } from "redux";
    import rootReducer from "./reducers";
    
    export default createStore(
      rootReducer,
      window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    );
    

    미들웨어를 설정해줘야 devtool을 사용할 수 있다.

    4. class형 컴포넌트를 함수형으로 만들고 useDispatch쓰기

    // 홈페이지 예제
    import React from "react";
    import { connect } from "react-redux";
    import { addTodo } from "../redux/actions";
    
    class AddTodo extends React.Component {
      constructor(props) {
        super(props);
        this.state = { input: "" };
      }
    
      updateInput = input => {
        this.setState({ input });
      };
    
      handleAddTodo = () => {
        this.props.addTodo(this.state.input);
        this.setState({ input: "" });
      };
    
      render() {
        return (
          <div>
            <input
              onChange={e => this.updateInput(e.target.value)}
              value={this.state.input}
            />
            <button className="add-todo" onClick={this.handleAddTodo}>
              Add Todo
            </button>
          </div>
        );
      }
    }
    
    export default connect(
      null,
      { addTodo }
    )(AddTodo);
    // export default AddTodo;
    

    이렇게 되어있는 코드를 함수형 컴포넌트로 만들면 아래와 같다.

    import { useDispatch } from "react-redux";
    import { addTodo } from "../redux/actions";
    import { useState } from "react";
    
    const AddTodo = () => {
      const dispatch = useDispatch();
      const [input, setInput] = useState("");
    
      const increment = () => {
        dispatch(addTodo(input));
        setInput("");
      };
    
      return (
        <div>
          <input onChange={(e) => setInput(e.target.value)} value={input} />
          <button className="add-todo" onClick={increment}>
            Add Todo
          </button>
        </div>
      );
    };
    
    export default AddTodo;
    

    원래 예시를 보면 connect에 mapDispatchToProps를 넘겨주고 있다. connect는 두번째 인자인 mapDispatchToProps위치에 dispatch를 넘겨주는데 이를 사용하는 함수를 원래 반환해준다. 

    const mapDispatchToProps = (dispatch) => {
      return {
        // dispatching plain actions
        increment: () => dispatch({ type: 'INCREMENT' }),
        decrement: () => dispatch({ type: 'DECREMENT' }),
        reset: () => dispatch({ type: 'RESET' }),
      }
    }

    하지만 우리는 이제 useDispatch hooks를 통해서 dispatch를 바로 가지고 오기때문에 여기다가 action을 반환하는 함수를 넘겨주면된다. 

    // class형일때
    handleAddTodo = () => {
        this.props.addTodo(this.state.input); // props로 넘겨준 addTodo를 이용해서 dispatch
        this.setState({ input: "" });
    };
    
    
    // 함수형일때
    const increment = () => {
      dispatch(addTodo(input));
      setInput("");
    };

     

    5. useSelector hooks 사용하기

    https://react-redux.js.org/api/hooks#useselector

     

    Hooks | React Redux

    Hooks

    react-redux.js.org

    여기에 따르면 useSelector는 대략 mapStateToProps랑 비슷한데, useSelector는 컴포넌트가 rerender될때마다 불릴거다(이전 값 reference랑 비교해서 변했을때). useSelector는 action이 dispatch되었을때, reference comparison을 해서 이전값에 비교해서 변했으면 rerender가 되도록 강제한다. 

    // 예제에있는 selectors.js, useSelector내에서 사용해서 data 모양을 바꾼다
    import { VISIBILITY_FILTERS } from "../constants";
    
    export const getTodosState = (state) => state.todos;
    
    export const getTodoList = (state) =>
      getTodosState(state) ? getTodosState(state).allIds : [];
    
    export const getTodoById = (state, id) =>
      getTodosState(state) ? { ...getTodosState(state).byIds[id], id } : {};
    
    export const getTodos = (state) =>
      getTodoList(state).map((id) => getTodoById(state, id));
    
    export const getTodosByVisibilityFilter = (state, visibilityFilter) => {
      const allTodos = getTodos(state);
      switch (visibilityFilter) {
        case VISIBILITY_FILTERS.COMPLETED:
          return allTodos.filter((todo) => todo.completed);
        case VISIBILITY_FILTERS.INCOMPLETE:
          return allTodos.filter((todo) => !todo.completed);
        case VISIBILITY_FILTERS.ALL:
        default:
          return allTodos;
      }
    };
    

    아래는 TodoList.js가 connect를 쓰는 것을  useSelector를 사용한것이다. 

    // connect쓸때
    
    import React from "react";
    import { connect } from "react-redux";
    import Todo from "./Todo";
    // import { getTodos } from "../redux/selectors";
    import { getTodosByVisibilityFilter } from "../redux/selectors";
    import { VISIBILITY_FILTERS } from "../constants";
    
    const TodoList = ({ todos }) => (
      <ul className="todo-list">
        {todos && todos.length
          ? todos.map((todo, index) => {
              return <Todo key={`todo-${todo.id}`} todo={todo} />;
            })
          : "No todos, yay!"}
      </ul>
    );
    
    const mapStateToProps = state => {
      const { visibilityFilter } = state;
      const todos = getTodosByVisibilityFilter(state, visibilityFilter);
      return { todos };
    };
    
    export default connect(mapStateToProps)(TodoList);
    
    // 함수형일때
    import Todo from "./Todo";
    import { useSelector } from "react-redux";
    import { getTodosByVisibilityFilter } from "../redux/selectors";
    
    const TodoList = () => {
      const todos = useSelector((state) =>
        getTodosByVisibilityFilter(state, state.visibilityFilter)
      );
      return (
        <ul className="todo-list">
          {todos && todos.length
            ? todos.map((todo, index) => {
                return <Todo key={`todo-${todo.id}`} todo={todo} />;
              })
            : "No todos, yay!"}
        </ul>
      );
    };
    
    export default TodoList;
    

     

    이렇게 해서 useSelector를 사용할 수 있다. 

     

    내 실습 코드

    https://github.com/hayoung0Lee/react-redux-hooks

     

    hayoung0Lee/react-redux-hooks

    Contribute to hayoung0Lee/react-redux-hooks development by creating an account on GitHub.

    github.com

     

     

    실습 완료

     

    후기

    redux도 redux인데 역시 엄청난 사람들이 예제를 만들어놔서 그런지 상태관리를 이렇게 해야하는구나 깨달을 수 있었다. Normalizr같은걸 안쓰고도 id를 뽑아서 object를 hash로 접근할수있게 구성하는건 정말 잘 기억을 해둬야겠다. 

    댓글

Designed by Tistory.