context가 가지고 있는 전역 데이터를 잘 활용하는 부분이 중요하게 됩니다.
그래서 context에 주어지는 전역 데이터를 어떻게 효과적으로 관리할 것인지에 대한 라이브러리
가 Redux
입니다.
store 안에 있는 상태를 보라색 공이 변경하면, 그 상태를 가지고 있는 다른 컴포넌트들이 자동으로 다시 렌더가 됩니다.
npx create-react-app redux-start
npm i redux
npm start
이렇게 만들어진 Action은 store에 전달이 되면 store에 있는 상태를 변경하는 용도로 사용이 됩니다.
store의 상태를 변경할 때 특별한 인자 값을 주지 않으면, 타입만 있는 액션이 전달이 됩니다.
액션을 전달하면서 액션이 전달하는 값을 이용하여 상태를 변경해야 한다면, 타입을 제외하고 다른 파라미터가 들어가게 됩니다.
액션 객체를 만들어 낼 때에는 리터럴을 사용하지 않고, 액션을 만들어내는 함수를 만들어서 사용하게 됩니다.(실수를 방지하기 위함)
function 액션생성자(...args) { return 액션; }
그래서 액션의 종류별로 액션생성함수가 똑같은 갯수로 만들어지게 됩니다.
액션을 준비하기 위해서는 2가지 단계가 있습니다.
애플리케이션에서 사용하는 모든 액션들을 정의하고, 액션 생성 함수를 만들어 넣습니다.
// 1. to do list 의 to do를 넣는 액션인 add to do 라는 액션의 타입을 정의하겠습니다.
const ADD_TODO = "ADD_TODO";
// 2. ADD_TODO 타입을 이용하는 액션 생성 함수를 한 개 만듭니다.
function addTodo(todo) {
return {
type: ADD_TODO,
todo,
};
}
// 이 함수는 전역 state의 todo라는 state 안에 새로운 todo를 넣을 것입니다.
// 액션을 생성할 때 이 addTo라는 함수를 이용해서 생성할 것이고, 그 때 인자로 할 일을 넘기면 됩니다.
// 여기까지는 리덕스의 어떤 라이브러리를 이용하지 않았습니다.
// 액션 객체와 액션 생성 함수를 만드는데 까지는 순수 javascript 입니다.
// 이 다음은 액션을 이용해서 상태를 변경할 수 있는 reducer에 대해서 배워보겠습니다.
function 리듀서(previousState, action) {
return newState;
}
리듀서 : 함수 이름
previousState : 현재의 state
action : 액션 객체
previousState 와 action 객체를 이용해서 새로운 state 객체를 만들어 내는 것이 리듀서의 역할입니다.
리듀서 함수를 만들어보겠습니다.
function todoApp(previousState, action) {
return previousState;
}
만약 어떠한 변화도 없게 위처럼 작성하면, 액션을 받더라도 아무 변화 없이 previousState가 return 되서 아무 변화도 일어나지 않습니다.
리듀서를 만들기 전에 먼저 state의 모습을 구상해보겠습니다.
state
['코딩', '점심 먹기'];
위처럼 state가 구성되어 있다고 생각하고, 아래처럼 리듀서를 작성해보겠습니다.
import { ADD_TODO } from "./actions";
function todoApp(previousState, action) {
// 초기값을 설정해주는 부분.
if (previousState === undefined) {
return [];
}
if (action.type === ADD_TODO) {
// ADD_TODO는 다른파일에서 정의했으므로 정의한 곳으로 가서 export하고, import로 가져와야합니다.
return [...previousState, action.todo];
}
// Immutable 하지 않다는 것은 예를 들면 이런것입니다.
// previousState.push(); // 객체를 변경하지만, 객체의 레퍼런스는 변경되지 않는 상황.
// 그러면 redux는 바뀌었다고 생각하지 않습니다.
// ADD_TODO 타입이 아닌 경우에는 아무 대응도하지 않고 현재값을 그대로 리턴하게 합니다.
return previousState;
}
설명하기 위해서 위처럼 코드를 작성했지만, 더 쉽게 하는 방법이 있습니다.
const initialState = []; // 보통은 먼저 초기 상태를 정의합니다.
function todoApp(previousState = initialState, action) {
// 초기값을 설정해주는 부분.
// if (previousState === undefined) {
// return [];
// }
if (action.type === ADD_TODO) {
// ADD_TODO는 다른파일에서 정의했으므로 정의한 곳으로 가서 export하고, import로 가져와야합니다.
return [...previousState, action.todo];
}
// Immutable 하지 않다는 것은 예를 들면 이런것입니다.
// previousState.push(); // 객체를 변경하지만, 객체의 레퍼런스는 변경되지 않는 상황.
// 그러면 redux는 바뀌었다고 생각하지 않습니다.
// ADD_TODO 타입이 아닌 경우에는 아무 대응도하지 않고 현재값을 그대로 리턴하게 합니다.
return previousState;
}
const initialState = [];
: 먼저 초기 상태를 정의하고,
function todoApp(previousState = initialState, action)
: 매개변수에서 default 값으로 초기화값을 할당합니다.
action과 마찬가지로 리듀서도 redux 라이브러리를 활용하는 부분은 아닙니다.
리듀서를 pure하게 만들고, Immutable 하게 처리가 되게 만들면 리듀서 함수의 작성이 끝납니다.
이러한 코드를 활용해서 redux의 store를 만들게 되면, 이러한 부분이 동작을 하게 됩니다.
이 다음부터는 store
를 만들어 보겠습니다.