[개발일지 40일차] React Redux

MSJ·2022년 6월 29일
0

WEB

목록 보기
40/41
post-thumbnail

2022-06-29

Redux 리덕스

JavaScript의 상태관리 라이브러리
React Context도 상태관리 툴 중의 하나. 그 외에 Mobx 등.

Redux의 본질은 Node.js모듈

상태 관리의 필요성

State는 component 안에서 관리된다.
Component간의 정보 공유에서 알아둘 것 :

  • 자식 컴포넌트들 간의 다이렉트 데이터 전달 불가능
    : 상태를 관리하는 부모 컴포넌트를 통해서 주고 받음
  • 자식들이 많아지면 상태관리 매우 복잡
    : 상태를 관리하는 상위 컴포넌트에서 계속 내려 받아야함
    => Props drilling 이슈

이러한 불편점을 해소시키는 것이 상태 관리 툴(라이브러리)

  1. 전역 상태 저장소를 제공한다.
  2. 어디서든 해당 저장소에 접근할 수 있다. (이 역할을 Reducer가 한다)
    : Props drilling 이슈 해결. 굳이 중간 지점에 존재하는 props들을 내려받지 않아도 됨

Redux의 세가지 원칙

  1. Single source of truth
  • 동일한 데이터는 항상 같은 곳에서 가지고 온다.
    :즉 스토어라는 하나뿐인 데이터 공간이 있다는 의미
  1. State is read-only
  • 리액트에서는 setState 메소드를 활용해야만 상태 변경이 가능
  • 리덕스에서도 액션이라는 객체를 통해서만 상태 변경 가능
  1. Changes are made with pure functions
  • 변경은 순수함수로만 가능
  • 리듀서와 연관되는 개념
  • Store - Action - Reducer

Store

상태가 관리되는 오직 하나의 공간

컴포넌트와 별개로 스토어라는 공간에 앱에서 필요한 상태를 담는다.
컴포넌트에서 상태 정보가 필요할 때 스토어에 접근

Action

- 앱에서 스토어에 운반할 데이터(주문서)
- 자바스크립트 객체 형식으로 되어있다

Reducer

- Action을 Store에 바로 전달하는 것이 아니라, Reducer에 전달해야함
Reducer가 주문을 보고 Store의 상태를 업데이트 한다.
Action을 Reducer에 전달하기 위해서는 dispatch() 메소르를 사용해야한다.

Action 객체dispatch() 메소드에 전달됨->dispatch(액션)을 통해 Reducer 호출-> Reducer는 새로운 Store생성

왜 이런가 하면, 데이터는 한 방향으로만 흘러야하기 때문


Provider란?

react-redux 라이브러리 안에 있는 컴포넌트.
리액트 앱에 스토어를 쉽게 연결하기 위한 컴포넌트

combineReducer란?

redux 모듈이 제공하는 함수
만든 모든 리듀서들을 통합하여 하나의 리듀서로 쓸 수 있음

useSelector란?

redux의 state 조회 (스토어에 있는 데이터들 조회)

useDispatch란?

생성한 action 실행


Redux 설치

  1. 명령프롬프트에서 프로젝트 폴더 접근
  2. npm install redux react-redux
    react-redux 명령어는 리액트 환경에 맞는 리덕스를 사용할 수 있게 해준다.

실습 1)

구현 과정 (원래는 구현 계획을 아래처럼 세부적으로 짜고 만들어야 적절한 지표가 된다)

  1. 왼쪽에는 연락처 등록폼, 오른쪽에는 연락처 리스트와 서치창
  2. 리스트에 유저 이름과 전화번호를 추가할 수 있다.
  3. 리스트에 아이템이 몇 개 있는지 보인다.
  4. 사용자가 유저를 이름 검색으로 찾을 수 있다.

App.js

import logo from './logo.svg';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Container, Row, Col } from 'react-bootstrap';
import ContactForm from './components/ContactForm';
import ContactList from './components/ContactList';

function App() {
  return (
    <div className="App">
      <h1 className="title">연락처</h1>
      <Container>
        <Row>
          <Col>
            <ContactForm></ContactForm>
          </Col>
          <Col>
            <ContactList></ContactList>
          </Col>
        </Row>
      </Container>
    </div>
  );
}

export default App;

App.css

title{
  text-align: center;
}

index.js

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

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

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

store.js

import { createStore } from 'redux';
import reducer from './reducer/reducer';

let store = createStore(reducer);

export default store;

components > ContactForm

import React, { useState } from 'react';
import { Form, Button } from 'react-bootstrap';
import { useDispatch } from 'react-redux';

const ContactForm = () => {
  const [name, setName] = useState('');
  const [phoneNumber, setPhoneNumber] = useState(0);
  const dispatch = useDispatch();

  const addContact = (event) => {
    event.preventDefault();
    dispatch({
      type: 'ADD_CONTACT',
      payload: { name, phoneNumber },
    });
  };

  return (
    <Form onSubmit={addContact}>
      <Form.Group className="mb-3" controlId="formName">
        <Form.Label>이름</Form.Label>
        <Form.Control
          type="text"
          placeholder="이름을 입력해주세요"
          onChange={(event) => setName(event.target.value)}
        />
      </Form.Group>
      <Form.Group className="mb-3" controlId="formContact">
        <Form.Label>전화번호</Form.Label>
        <Form.Control
          type="number"
          placeholder="전화번호를 입력해주세요"
          onChange={(event) => setPhoneNumber(event.target.value)}
        />
      </Form.Group>
      <Button variant="primary" type="submit">
        추가
      </Button>
    </Form>
  );
};

export default ContactForm;

components > ContactList

import React from 'react';
import SearchBox from './SearchBox';
import ContactItem from './ContactItem';
import { useSelector } from 'react-redux';

const ContactList = () => {
  const contactList = useSelector((state) => state.contactList);
  return (
    <div>
      <SearchBox />
      {contactList.map((item) => (
        <ContactItem item={item} />
      ))}
    </div>
  );
};

export default ContactList;
import React from 'react';
import { Row, Col, Form, Button } from 'react-bootstrap';

const SearchBox = () => {
  return (
    <Row>
      <Col lg={10}>
        <Form.Control type="text" placeholder="이름을 입력해주세요" />
      </Col>
      <Col lg={2}>
        <Button>찾기</Button>
      </Col>
    </Row>
  );
};

export default SearchBox;

component > ContactItem

import React from 'react';
import { Row, Col } from 'react-bootstrap';

const ContactItem = ({ item }) => {
  return (
    <Row>
      <Col lg={1}>
        <img
          width={50}
          src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Unknown_person.jpg/925px-Unknown_person.jpg"
          alt=""
        />
      </Col>
      <Col lg={11}>
        <div>{item.name}</div>
        <div>{item.phoneNumber}</div>
      </Col>
    </Row>
  );
};

export default ContactItem;

reducer > reducer (양이 늘어날수도 있어서 보통 폴더에 집어넣음)

let initialState = {
  contactList: [],
};

function reducer(state = initialState, action) {
  const { type, payload } = action;
  switch (type) {
    case 'ADD_CONTACT':
      return {
        ...state,
        contactList: [
          ...state.contactList,
          {
            name: action.payload.name,
            phoneNumber: action.payload.phoneNumber,
          },
        ],
      };
    default:
      return { ...state };
  }
}

export default reducer;

어려웠던 점

추가하는 것 자체는 기능하는데 입력한 값이 제대로 전해지지 않음

해결 방법

ContactForm.js 에 부트스트랩에서 쓰던 명칭 formPassword를 formContact로 바꾸어주니 잘 나오는 줄 알았는데,

  <Form.Group className="mb-3" controlId="formContact">

알고보니 전화번호 입력칸에 number값을 줘서 하이픈(-)을 넣었을 때 추가 버튼이 작동하지 않던 거였다.

소감

하하하... 오류 찾기 즐겁다...~

profile
제로부터 시작하는 프로그래밍&코딩

0개의 댓글