[TIL] React 공부일기 #7

Hyeonu_J·2021년 8월 18일
0
post-thumbnail
post-custom-banner

Context API

리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능
이전에 사용한 리액트 라우터, styled-components등의 라이브러리가 이 기능을 기반으로 구현되있다.

일반적인 전역 상태 관리 흐름에선 여러 컴포넌트를 거쳐 props로 원하는 상태와 함수를 전달했지만, context API를 사용하면 Context를 만들어 단 한번에 원하는 값을 받아와서 사용할 수 있다.

쉽게 리액트에서 Context는 하나의 전역 공간이라고 보면 될 것 같다.

context

새 context를 만들때는 createContext 함수를 사용한다.

예제:
color.js

import {createContext} from 'react';
const ColorContext = createContext({color:'black'});
export default ColorContext;

Consumer

책에는 설명이 없었는데
전역 데이터를 하위 컴포넌트에서 접근하는 컴포넌트..인듯?

예제:
ColorBox.js

import React from 'react';
import ColorContext from '../contexts/color';

const ColorBox = () => {
    return (
        <ColorContext.Consumer>
            {value => (
                <div
                    style={{
                        width:'64px'
                        height:'64px'
                        background:value.color
                    }}
                />
            )}
        </ColorContext.Consumer>
    );
};

export default ColorBox;
                      

참고 :
위의 코드처럼 컴포넌트의 children이 있어야 할 자리에
일반 JSX 혹은 함수를 전달하는 것을 Render Props라고 한다.

Provider

Provider 를 사용하면 Context의 value를 변경할 수 있다.
사용할 땐 value 값을 제대로 명시해 주자!

예제:
App.js

import React from 'react'
import ColorBox from './components/ColorBox';
import ColorContext from './contexts/color';
const App = () => {
    return (
    	<ColorContext.Provider value={ {color:'red'} }>
            <div>
                <ColorBox/>
            </div>
        </ColorContext.Provider>
    )
}

export default App;

useContext Hook

함수형 컴포넌트에서 Context를 아주 편하게 사용할 수 있다.
아래 수정전 ColorBox.js 코드와 아래 새로운 ColorBox.js 코드와 비교해보자.

예제:
수정전 ColorBox.js

import React from 'react'
import {ColorConsumer} from '../contexts/color';

const ColorBox = () => {
    return (
    	<ColorConsumer>
            {value => (
                <>
                    <div
                        style={{
                            width:'64px',
                            height:'64px',
                            background:state.color
                        }}
                    />
                    <div
                        style={{
                            width:'32px',
                            height:'32px',
                            background:state.subcolor
                        }}
                    />
                </>
            )}
        </ColorConsumer>
    )
}
export default ColorBox;

새로운 ColorBox.js

import React,{useContext} from 'react'
import ColorContext from '../contexts/color';

const ColorBox = () => {
    const {state} = useContext(ColorContext);
    return (
        <>
            <div
                style={{
                    width:'64px',
                    height:'64px',
                    background:state.color
                }}
            />
            <div
                style={{
                    width:'32px',
                    height:'32px',
                    background:state.subcolor
                }}
            />
        </>
    )
}
export default ColorBox;

Render Props 패턴이 불편하다면, useContext Hook을 사용해 훨씬 편하게 Context값을 조회할 수 있다!!


정리

전역 state와 props를 교류할 일이 있으면 Context API를 사용하자.

기존에는 컴포넌트 간에 상태를 교류해야 할 때 무조건 부모 → 자식 흐름으로 props를 통해 전달해 주었지만, Context API를 통해 더욱 쉽게 상태를 교류할 수 있게 되었다.
컴포넌트의 개수가 많은 상황이라면, 이 Context API를 활용해야겠다.

P.S 언젠가 만들 개인 프로젝트 만들땐 안쓸거 같다..


리덕스

리액트 상태 관리 라이브러리. 가장 많이 사용하는 상태 관리 라이브러리..!

Context API 와 동일하게 전역 상태를 관리한다.
전역 상태 관리만 보면 Context API 만 써도 충분한데, 리덕스를 사용하면 상태를 더욱 체계적으로 관리할 수 있다.

개념정리

액션

상태에 어떠한 변화가 필요하면 액션이라는 것이 발생한다. 이는 하나의 객체로 표현된다.

{
    type: 'ADD_TODO',
    data: {
    	id: 1,
        text: '리덕스 배우기'
    }
}

{
    type: 'CHANGE_INPUT',
    text: '안녕하세요'
}

액션 객체는 type 필드를 반드시 가지고 있어야 한다. 이 값을 액션의 이름이라 생각하자.
그외 값들은 맘대로! 액션 이름은 고유해야 된다!
주로 대문자로 작성한다.


액션 생성 함수

설명이 필요한가? 말 그대로..
어떤 변화를 일으켜야 할 때마다 액션 객체를 만들어야 하는데, 이를 함수로 만들어 관리할 수 있다.

const changeInput = text => ({
    type: 'CHANGE_INPUT',
    text
})

리듀서

변화를 일으키는 함수.
액션을 만들어서 발생시키면 리듀서가 현재 상태와 전달받은 액션 객체를 파라미터로 받아 온다.
그리고 두 값을 참고하여 새로운 상태를 만들어 반환해 준다.

const initialState = { counter:1 }

function reducer(state = initialState, action) {
    switch(action.type) {
        case INCREMENT:
            return {
            	counter:state.counter+1
            }
        default:
            return state;
    }
}

스토어

프로젝트에 리덕스를 적용하기 위해 스토어를 만든다.
한 개의 프로젝트는 단 하나의 스토어만 가질 수 있다.


디스패치

스토어의 내장 함수 중 하나.
액션을 발생시키는 것이라 이해하자.
dispatch(action)과 같은 형태로 액션 객체를 파라미터로 넣어서 호출한다.
이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜서 새로운 상태를 만들어 준다.


구독(subscribe)

이것도 스토어의 내장 함수 중 하나.
subscribe 함수 안에 어떤 함수를 파라미터로 넣어 호출해 주면,
그 함수가 액션이 디스패치되어 상태가 업데이트 될 때마다 호출된다.


예제

리액트가 아닌 자바스크립트 파일에 실습한 파일이다. 대략 어떻게 쓰는지 알아야긋다.

//DOM 레퍼렌스 만들기
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');
import {createStore} from 'redux'

//액션 타입과 액션 생성 함수 정의
const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';

//액션 생성 함수
const toggleSwitch = () => ({type:TOGGLE_SWITCH});
const increase = difference => ({type:INCREASE,difference});
const decrease = () => ({ type:DECREASE })

//초깃값
const initialState = {
    toggle:false,
    counter: 0
}

//리듀서 함수 정의
function reducer(state = initialState, action) {
    switch (action.type){
        case TOGGLE_SWITCH:
            return {
                ...state,
                toggle: !state.toggle
            };
        case INCREASE:
            return {
                ...state,
                counter:state.counter+action.difference
            }
        case DECREASE:
            return {
                ...state,
                counter:state.counter-1
            }
        default:
            return state;
    }
}

//스토어 만들기
const store = createStore(reducer);

//render 함수 만들기
const render = () => {
    const state = store.getState() //현재 상태 불러오기
    if (state.toggle) {
        divToggle.classList.add('active');
    } else {
        divToggle.classList.remove('active')
    }
    counter.innerText = state.counter
}


render();
store.subscribe(render) //구독

//디스패치로 액션 발생시키기
divToggle.onclick = () => {
    store.dispatch(toggleSwitch())
}
btnIncrease.onclick = () => {
    store.dispatch(increase(1));
}
btnDecrease.onclick=() => {
    store.dispatch(decrease())
}
profile
흔한 컴공러 / 3학년
post-custom-banner

0개의 댓글