리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능
이전에 사용한 리액트 라우터, styled-components등의 라이브러리가 이 기능을 기반으로 구현되있다.
일반적인 전역 상태 관리 흐름에선 여러 컴포넌트를 거쳐 props로 원하는 상태와 함수를 전달했지만, context API를 사용하면 Context를 만들어 단 한번에 원하는 값을 받아와서 사용할 수 있다.
쉽게 리액트에서 Context는 하나의 전역 공간이라고 보면 될 것 같다.
새 context를 만들때는 createContext 함수를 사용한다.
예제:
color.js
import {createContext} from 'react';
const ColorContext = createContext({color:'black'});
export default ColorContext;
책에는 설명이 없었는데
전역 데이터를 하위 컴포넌트에서 접근하는 컴포넌트..인듯?
예제:
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 를 사용하면 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;
함수형 컴포넌트에서 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 함수 안에 어떤 함수를 파라미터로 넣어 호출해 주면,
그 함수가 액션이 디스패치되어 상태가 업데이트 될 때마다 호출된다.
리액트가 아닌 자바스크립트 파일에 실습한 파일이다. 대략 어떻게 쓰는지 알아야긋다.
//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())
}