[fastcampus] Ch11. Redux로 상태관리하기(3)

productuidev·2022년 7월 27일
0

React Study

목록 보기
52/52
post-thumbnail
post-custom-banner

1. Redux Basic

2) Redux를 React에 연결(2)

  • 지금까지 만든 프로젝트를 React에 연결하기
  • react-redux를 쓰고 연결하기

(1) react-redux

  • Provider 컴포넌트 제공
  • connect 함수(HOC)를 통해 Container를 만들어줌 (HOC는 보통 로직을 만들어서 props로 넣어줌)
  • Container는 store의 statedispatch(action)를 연결한 컴포넌트에 props로 넣어주는 역할

그렇다면 필요한 것은?

  • 어떤 state를 어떤 props에 연결할 것인지에 대한 정의
  • 어떤 dispatch(action)을 어떤 props에 연결할 것인지에 대한 정의
  • 그 props를 보낼 컴포넌트를 정의

react-redux의 Provider import 후 Provider 컴포넌트로 변경

src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';

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

HOC로 TodoList 연결하기

src/components/TodoList.jsx

import { connect } from 'react-redux';

// 위쪽으로 이동
// Props로 todos를 받아서 map
function TodoList({todos}) {
  return (
    <ul>
      {todos.map((todo)=>{
        return <li>{todo.text}</li>;
      })}
    </ul>
  );
}

// react-redux의 함수 사용하기 위해 가져와서 실행
// 실행한 결과물이 HOC 함수가 되므로 다시 한번 실행
// 실행한 결과가 Container
// 만들어진 TodoListContainer를 export default

// state 객체를 받아서 Props로 만들기
const mapStateToProps = (state) => {
  return {
    todos: state.todos,
  };
};

// dispatch 객체를 받아서 Props로 만들기
const mapDispatchToProps = (dispatch) => {
  return {};
};

// connect가 config하는 인자 2개 함수
// HOC의 인자
const TodoListContainer = connect(mapStateToProps,mapDispatchToProps)(TodoList);

export default TodoListContainer;

src/components/TodoForm.jsx

import { useRef } from "react";
import { connect } from 'react-redux';
import { addTodo } from "../redux/actions";

// 위쪽으로 이동
// TodoForm은 함수를 받아서 보여주는 역할(Presentational Component)
// Container가 주는 data를 받아서 그냥 보여주거나 실행함
function TodoForm({ add }) {
  const inputRef = useRef();

  return (
    <div>
      <input ref={inputRef} /> <button onClick={click}>추가</button>
    </div>
  );

  // 버튼 클릭 시 addTodo 액션
  // dispatch
  function click() {
    // 직접 dispatch하지 않고 add를 props으로 받아서 실행
    add(inputRef.current.value);
  }
}

// Container = Smart Component로 역할 분리
// Store와 Presentatinal Component를 이어주는 역할 = connect
export default connect(
  (state) => ({}),

  // todos를 실행하는 로직
  (dispatch) => ({
    add: (text) => {
      dispatch(addTodo(text));
    }
  })
)(TodoForm);

(2) Container로 분리(이동)

  • src/containers/TodoListContainer.jsx 생성 > TodoList 컴포넌트 수정
  • src/containers/TodoFormContainer.jsx 생성 > TodoForm 컴포넌트 수정

src/containers/TodoFormContainer.jsx

import { connect } from 'react-redux';
import { addTodo } from "../redux/actions";
import TodoForm from '../components/TodoForm';

// 정확한 Container로 분리
// Container = Smart Component로 역할 분리
// Store와 Presentatinal Component를 이어주는 역할 = connect

const TodoFormContainer = connect(
  (state) => ({}),

  // todos를 실행하는 로직
  (dispatch) => ({
    add: (text) => {
      dispatch(addTodo(text));
    }
  })
)(TodoForm);

export default TodoFormContainer;

src/components/TodoForm.jsx

import { useRef } from "react";

export default function TodoForm({ add }) {
  const inputRef = useRef();

  return (
    <div>
      <input ref={inputRef} /> <button onClick={click}>추가</button>
    </div>
  );

  function click() {
    add(inputRef.current.value);
  }
}

src/containers/TodoListContainer.jsx

import { connect } from 'react-redux';
import TodoList from '../components/TodoList';

const mapStateToProps = (state) => {
  return {
    todos: state.todos,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {};
};

const TodoListContainer = connect(mapStateToProps,mapDispatchToProps)(TodoList);

export default TodoListContainer;

src/components/TodoList.jsx

export default function TodoList({todos}) {
  return (
    <ul>
      {todos.map((todo)=>{
        return <li>{todo.text}</li>;
      })}
    </ul>
  );
}

App.js로 가서 컨테이너로 수정

src/App.js

import logo from './logo.svg';
import './App.css';
import TodoListContainer from './containers/TodoListContainer';
import TodoFormContainer from './containers/TodoFormContainer';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <TodoListContainer />
        <TodoFormContainer />
      </header>

    </div>
  );
}

export default App;

(3) Container를 Hook으로 변경하기

HOC로 공통로직을 제공하는 것도 Hook으로 많이 바뀌었음

src/containers/TodoListContainer.jsx

import { useSelector } from 'react-redux';
import TodoList from '../components/TodoList';

// jsx를 리턴하는 컴포넌트
// TodoListContainer가 하는 일 : store 연결 > data 꺼내서 > 필요한 것을 props로 넣어주기
// useSelector hook을 사용하면 좀 더 편하게 하는 일을 명시적으로 보여줄 수 있음
// 하위 컴포넌트에 찔러넣어주는 형태로 변경
function TodoListContainer() {
  const todos = useSelector((state)=>state.todos);

  return <TodoList todos={todos} />;
}

export default TodoListContainer;

src/containers/TodoFormContainer.jsx

import { useCallback } from "react";
import { addTodo } from "../redux/actions";
import { useDispatch } from 'react-redux';
import TodoForm from '../components/TodoForm';

// useDispatch hook 활용
// 실행될 때마다 add에 새로운 함수를 찔러넣어주므로
// useCallback으로 처리 (dispatch 함수가 deps하여 새로 만들어질 때 다시 만들어짐)
// store가 달라지지 않으므로 처음에 만든 것과 똑같은 형식 (불필요하게 새로 만들어지지 x)
export default function TodoFormContainer() {
  const dispatch = useDispatch();

  const add = useCallback((text)=>{
    dispatch(addTodo(text));
  }, [dispatch]);

  return <TodoForm add={add} />
};

결과

react-redux3

참고 자료 & 공부 (개념 이해)

profile
필요한 내용을 공부하고 저장합니다.
post-custom-banner

0개의 댓글