[React] 클래스형 컴포넌트 - Redux store

moongyu·2021년 8월 6일
0

React

목록 보기
2/4
post-thumbnail

리덕스(Redux)란?

  • JavaScript 앱을 위한 예측 가능한 state 컨테이너이다.
    애플리케이션이 일관되게 동작하는 데 도움을 주며, 여러 환경(client, server, native 등)에서 쉽게 테스트해볼 수 있다. React 외에도 Angula, Vue와 같이 여러 프레임워크에서 사용되고 있다.

    → 쉽게 말하면 Redux를 사용하면 하나의 저장소에서 state를 공유하고 관리할 수 있다. 이를 통해 서로 다른 컴포넌트에서 공통된 객체의 상태를 쉽게 조회할 수 있다는 장점이 있다.

  • 공식 문서에서는 다음과 같은 경우에 사용하는 것을 추천한다.

    • 특정 state를 앱의 여러 위치에서 사용해야할 때
    • 앱의 state가 자주 변경될 때
    • 해당 state를 업데이트 하는 로직이 복잡할 때
    • 앱의 규모가 크고 여러 사람이 함께 작업할 때
    • 해당 state의 업데이트 과정을 추적할 필요가 있을 때

  • 지금까지 사용해본 경험으로는, 정말 state 관리하기에는 최고의 라이브러리인 것 같다. 어디에서든지 편하게 state를 가져올 수 있기 때문에 개발하는 로직이 많이 단순해진다. 단점은 초기 진입 장벽이 비교적 높은 편인 것 같고, 공식 문서에서도 Vanilla JS와 React에 완전히 익숙해진 후 사용하는 것을 권한다고 한다. 개인적으로는 프로젝트 진행을 위해 가져온 보일러플레이트에 있는 바람에 사용하게 되었다.

1. 클래스형 컴포넌트에서 Redux 사용하기

  • 처음에는 React Hook처럼 함수형 컴포넌트에서만 사용 가능한 기능인 줄 알았다. 그런데 개발 과정에서 클래스형 컴포넌트에서 꼭! Redux Store에 저장되어있는 state를 조회해야하는 일이 생겼고 열심히 구글링을 해보니 클래스형 컴포넌트에서도 state 조회가 가능했다!! 😆

  • 대략적인 사용 방법은 다음과 같다.

    import { connect } from 'react-redux';
	
    class BoardDetail extends Component {

      ...
      
      const {storeUser} = this.props;

        ...

      render() {
        ...
      }
    }

    const mapStateToProps = (state) => ({
      storeUser: state.user.userData
    });

    export default connect(mapStateToProps)(BoardDetail)

1) 먼저 connect 함수를 import 해준다. 이 함수를 통해 React 컴포넌트와 Redux store를 연결해줄 수 있다.

2) 그 후 store에서 원하는 데이터를 가져올 수 있다. 이때 state를 다루기 위한 부분이 mapStateToProps라는 함수이다. 말 그대로 원하는 stateprops로 넘겨준다. 이때 반환형은 객체이다.

3) 이렇게 받아온 propsthis.props로 조회하여 원하는 객체에 저장할 수 있다.

4) 추가적으로, 만약 store에 저장된 state에 action을 주고 싶다면 mapDispatchToProps라는 함수를 사용해주면 된다!

💎 위와 같은 방법을 통해 웹페이지 게시판 기능에서 로그인한 유저와 글쓴이의 id를 비교하여 수정/삭제 권한을 부여하는 것이 가능해졌다 !


2. 전체 코드

import React, {Component} from 'react';
import { Table, Button } from "react-bootstrap";
import {NavLink} from 'react-router-dom';
import { connect } from 'react-redux'
import axios from 'axios';
import './css/BoardDetail.css'

class BoardDetail extends Component {
    constructor(props) {
        super(props);
        this.state = {board: []};
        this.state = {writer: ""};
        this.state = {isSame: false};
      }
    
    componentDidMount() {
        if (this.props.location.query !== undefined) {
            this.getDetail();
        } else {
            window.location.href="/board";
        }
    }

    deleteBoard = _id => {
        const send_param = {
            _id
        };

        if (window.confirm("정말 삭제하시겠습니까?")) {
            axios
                .post("/api/board/delete", send_param)
                //정상!
                .then(returnData => {
                    if(returnData.data.success){
                        alert("삭제되었습니다.");
                        window.location.href="/";
                    } else {
                        alert("삭제에 실패했습니다.")
                    }
                })
                //에러
                .catch(err => {
                    console.log(err);
                });
        }
    };

    getDetail = () => {
        const send_param = {
            _id: this.props.location.query._id //게시물 고유번호
        };
        const marginBottom = {
            marginBottom: 5
        };
        axios
            .post("/api/board/detail", send_param)
            //정상!
            .then(returnData => {
                if (returnData.data.board[0]) { //해당 게시물이 있으면
                    
                    // 게시물 writer에게만 수정/삭제 버튼 보여주기
                    const {storeUser} = this.props; //현재 로그인한 유저 _id
                    this.setState({writer: returnData.data.board[0].writer}) //게시물 쓴 사람 _id
                    
                    // 로그인 된 상태이고 && 로그인한 유저 === 게시물 쓴 사람이면
                    if(storeUser.hasOwnProperty('_id') && storeUser._id === this.state.writer){
                        this.setState({isSame: true});
                    } else {
                        this.setState({isSame: false});
                    }

                    const board = (
                        <div style={{width:'60%', margin:'auto'}}>
                            <Table striped bordered hover>
                                <thead>
                                    <tr>
                                        <th id='title' className='content'>{returnData.data.board[0].title}</th>
                                    </tr>
                                    <tr>
                                        <td id='writer' className='content'>
                                            <span>{`Writer  `}</span>
                                            {returnData.data.board[0].name}
                                        </td>
                                    </tr>
                                    <tr>
                                        <td id='date' className='content'>
                                            <span>{`Date  `}</span>
                                            {returnData.data.board[0].createdAt.substring(0, 10)}
                                        </td>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr>
                                        <td className='content' colSpan="2" dangerouslySetInnerHTML={{
                                            __html: returnData.data.board[0].content
                                        }}/>
                                    </tr>
                                </tbody>
                            </Table>
                            <div>
                            <br/>
                        <NavLink
                                to={{
                                    pathname: "/board/write",
                                    query: {
                                        title: returnData.data.board[0].title,
                                        content: returnData.data.board[0].content,
                                        _id: this.props.location.query._id
                                    }
                                }}
                        >
                            <Button block style={marginBottom, { display : `${this.state.isSame ? 'inline-block' : 'none'}`}}>
                                글 수정
                            </Button>
                        </NavLink>
                        <Button
                            style={{ display : `${this.state.isSame ? 'inline-block' : 'none'}`}}
                            block
                            onClick={this.deleteBoard.bind(
                                null, this.props.location.query._id
                            )}>
                                글 삭제
                            </Button>
                            </div>
                        </div>
                    );
                    this.setState({board: board});
                }
            })
            //에러
            .catch(err => {
                console.log(err);
            });
    };

    render() {
        const divStyle = {
            margin: 50
        };

        return <div style={divStyle}>{this.state.board}</div>;
    }
}

const mapStateToProps = (state) => ({
    storeUser: state.user.userData
  });
  
export default connect(mapStateToProps)(BoardDetail)


References

https://react-redux.js.org/introduction/getting-started
https://redux.js.org/faq/general#when-should-i-use-redux
https://react-redux.js.org/api/connect

profile
개발 블로그 (●'◡'●)

0개의 댓글