상태란 컴포넌트 내부에서 관리되며 애플리케이션의 렌더에 영향을 미치는 플레인 자바스크립트 객체다. 변화하는 데이터라고도 한다.
지역 상태는 특정 컴포넌트 안에서만 관리되는 상태를 뜻한다.
다른 컴포넌트들과 데이터를 공유하지 않는다.
예를 들면 input, selectbox 등에서 사용자의 입력값을 받는 경우가 있다.
컴포넌트 간 상태는 여러 가지 컴포넌트에서 관리되는 상태를 나타낸다.
다수의 컴포넌트에서 쓰이고, 또 영향을 미치는 상태를 뜻한다.
예를 들면, 프로젝트 곳곳에 쓰이는 모달이 있다.
보통 상위 컴포넌트에서 하위 컴포넌트로 props를 넘겨 해당 컴포넌트까지 전달되도록 하는 props drilling 방식을 필요로 한다.
전역 상태는 프로젝트 전체에 영향을 끼치는 상태다.
이 또한 props drilling 방식을 활용해서 부모에서 자식으로 데이터를 전달한다.
서로 다른 두 컴포넌트에 같은 데이터가 필요한 경우 각 컴포넌트가 부모 자식 관계로 되어 있지 않은 이상, 각 컴포넌트 간의 직접적인 데이터 전달이 어렵다.
데이터를 부모 컴포넌트로 보내고 다시 그 데이터가 필요한 컴포넌트로 전달해야 하는데, 이러한 props drilling이 많아지면 props를 추적하기 어려워진다.
따라서 각 어플리케이션에 알맞은 상태관리 툴을 선택해 상태를 잘 관리하는 것이 중요하다.
Context API는 React 컴포넌트 트리 안에서 전역 상태를 공유할 수 있도록 만들어진 방법이다.
Context API는 종속성을 주입하기 위한 도구이기 때문에 전역 상태관리 툴이라기엔 다소 애매한 면이 있다.
Context API는 이미 존재하는 상태를 다른 컴포넌트들과 쉽게 공유할 수 있게 해주는 역할을 한다.
import {createContext} from 'react';
const MyContext = createContext();
createContext
함수를 불러와서 Context를 만든다.
기본 값을 설정하고 싶은 경우 createContext 함수 안에 인자로 기본 값을 넣어주면 된다.
function App() {
return (
<MyContext.Provider value="Hello World">
<GrandParend />
</MyContext.Provider>
);
}
Context 객체 안에 Provider라는 컴포넌트가 있다.
컴포넌트 간에 공유하고자 하는 값을 value라는 props로 설정하면 자식 컴포넌트들에서 해당 값에 접근할 수 있다.
import {useContext} from 'react';
function Message() {
const value = useContext(MyContext);
return <div>Received: {value}</div>;
}
useContext를 사용하여 Context에 넣은 값에 바로 접근할 수 있다.
Redux는 전역 상태관리를 위한 도구로, 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너다.
Store
Store는 상태가 관리되는 오직 하나의 공간이다.
컴포넌트와는 별개로 스토어라는 공간이 있어 그 스토어 안에 앱에서 필요한 상태를 담는다.
컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.
Action
Action은 앱에서 스토어에 운반할 데이터를 말한다.
Action은 자바스크립트 객체 형식으로 되어 있다.
Reducer
Action을 Reducer에 전달해야 Store에 저장할 수 있다.
Reducer가 Action을 보고 Store의 상태를 업데이트 한다.
Action을 Reducer에 전달하기 위해서는 dispatch() 메소드를 사용해야 한다.
- Action 객체가 dispatch() 메소드에 전달된다.
- dispatch(Action)를 통해 Reducer를 호출한다.
- Reducer는 새로운 Store를 생성한다.
Reudx의 데이터 흐름은 동일하게 단방향으로, view(컴포넌트)에서 Dispatch()라는 함수를 통해 Action이 발동되고 Reducer에 정의된 로직에 따라 Store의 State가 변화하고 그 State를 쓰는 view(컴포넌트)가 변하는 흐름을 따른다.
[reducers / index.js]
import {combineReducers} from 'redux';
import counter from './counter';
// 여러 reducer를 사용할 경우 reducer를 하나로 묶어주는 메소드다.
// store에 저장되는 redcuer는 한 개다.
const rootReducer = combineRudecers({
counter
});
export default rootReducer;
rootReducer를 정의한다.
[reducers / counter.js]
export const INCRESE = "COUNT/INCRESE";
export const increaseCount = count => ({ type: INCRESE, count});
const initalState = {
count: 0;
};
const counter = (state = initalState, action) => {
switch (action.type) {
case INCRESE:
return {
...state,
count: action.count
};
// default를 쓰지 않으면 맨 처음 state에 count값이 undefined가 나오므로 default문을 꼭 넣어야 한다.
default:
return state;
}
};
세부 reducer를 정의한다.
[index.js]
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './App';
import rootReducer from './reducers';
// 위에서 만든 reducer를 스토어를 만들 때 넣어준다.
const store = createStore(rootReducer)
ReactDOM.render(
//만든 store를 앱 상위에 넣어준다.
<Provider store = {store}>
<App />
</Provider>
document.getElementById('root'),
);
App에 sotre를 넣고, 만든 reducer를 반영한다.
import {useSelector, useDispatch} from 'react-redux';
import {increseCount} from 'reducers/count';
const dispatch = useDispatch();
const {count} = useSelector(state => state.counter);
const increase = () => {
return (
<div>
{count}
<button onCLick={increase}>증가</button>
</div>
);
};
export default Counter;
Store에서 useDispatch, useSelector로 state와 함수를 가져와서 컴포넌트에서 redux를 사용한다.
Zustand는 독일어로 '상태'라는 뜻을 가진 라이브러리이며 Jotai를 만든 카토 다이시가 제작에 참여하고 적극적으로 관리하는 라이브러리다. 다음과 같은 특징을 가지고 있다.
[store.js]
1. Store 생성
import create from 'zustand';
const useStore = create(set => ({
bears: 0,
increasePopulation: () => set(state => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 })
}));
export default useStore
bears
라는 초깃값을 선언하고 값을 조작하는 increasePopulation과 removeAllBears를 선언한다.
이 때 set
을 활용한다.
import useStore from '../store.js';
const App = () => {
const { bears, increasePopulation, removeAllBears } = useStore(state => state);
return (
<>
<h1>{bears} around here ...</h1>
<button onClick={increasePopulation}>one up</button>
<button onClick={removeAllBears}>remove all</button>
</>
)
};
Store에 선언한 값과 메서드를 useStore를 통해 불러와서 아주 간단하게 사용할 수 있다.