๊ทธ๋์ ๋ด ์ํ ์๋์ ์ค์ฒฉ ๋ ๊ตฌ์กฐ๊ฐ ์์ต๋๋ค.
state = {
plans: [
{title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
{title: 'B', exercises: [{title: 'exe5'}, {title: 'exe1'},{title: 'exe2'}]}
]
}
์ด์ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ง ์๋ ๋ฆฌ๋์๋ฅผ ๋ง๋ค๋ ค๊ณ ํ๋๋ฐ,์ด ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์์ ๋ด๊ณ ๋๋จธ์ง ์ฑ์ ์ฝ๋ฉํ๋ ๋ฐ ๋ ๋ง์ ์๊ฐ์ ์๋นํ๋ ์์ ์ ์ด๋ฅด๋ ์ต๋๋ค.
์๋ฅผ ๋ค์ด ์๋ก์ด ๋น ์ด๋์ ์ถ๊ฐํ๊ฑฐ๋ ๊ธฐ์กด ์ด๋์ ์ ๋ฐ์ดํธํ๋ ค๋ฉด ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ฉด๋ฉ๋๋ค.
state.plans[planIdx].exercises.push({})
state.plans[planIdx].exercises[exerciseIdx] = exercise
ํ์ง๋ง์ด ์ค์ฒฉ ๊ตฌ์กฐ์์ ๋์ผํ ์์ ์ ์ํํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น์? Redux ๋ฌธ์์ ๋ฌธ์ ํด๊ฒฐ ๋ถ๋ถ์ ์ฝ์์ง๋ง ๊ฐ์ฅ ๋จผ ๊ณณ์์ ๊ณํ์ ์ ๋ฐ์ดํธํ์ต๋๋ค.
case 'UPDATE_PLAN':
return {
...state,
plans: [
...state.plans.slice(0, action.idx),
Object.assign({}, state.plans[action.idx], action.plan),
...state.plans.slice(action.idx + 1)
]
};
์ด๊ฒ์ผ๋ก ์์ ํ๋ ๋ ๋น ๋ฅธ ๋ฐฉ๋ฒ์ด ์์ต๋๊น? ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผํ๊ฑฐ๋ ์ ์ด๋ ๋๊ตฐ๊ฐ๊ฐ์ด ๋ฌธ์ ๋ฅผ ๋ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํด ์ค ์ ์๋ค๋ฉด ...
๊ฐ์ฌํฉ๋๋ค!
https://github.com/gaearon/normalizr ๊ณผ ๊ฐ์ด ์ค์ฒฉ ๋ JSON์ ์ ๊ทํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์, ๋ฐ์ดํฐ๋ฅผ ์ ๊ทํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ด๋ ๊ฒํ๋ฉด "๊น์ด"๊ฐ ํ์๊ฐ ์์ต๋๋ค. ๋ชจ๋ ์ํฐํฐ๊ฐ ๋์ผํ ์์ค์ ์์ต๋๋ค.
๊ทธ๋์ ๋น์ ์ ์ํ๋
{
entities: {
plans: {
1: {title: 'A', exercises: [1, 2, 3]},
2: {title: 'B', exercises: [5, 1, 2]}
},
exercises: {
1: {title: 'exe1'},
2: {title: 'exe2'},
3: {title: 'exe3'}
}
},
currentPlans: [1, 2]
}
๊ฐ์๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ์ ์์ต๋๋ค.
import merge from 'lodash/object/merge';
const exercises = (state = {}, action) => {
switch (action.type) {
case 'CREATE_EXERCISE':
return {
...state,
[action.id]: {
...action.exercise
}
};
case 'UPDATE_EXERCISE':
return {
...state,
[action.id]: {
...state[action.id],
...action.exercise
}
};
default:
if (action.entities && action.entities.exercises) {
return merge({}, state, action.entities.exercises);
}
return state;
}
}
const plans = (state = {}, action) => {
switch (action.type) {
case 'CREATE_PLAN':
return {
...state,
[action.id]: {
...action.plan
}
};
case 'UPDATE_PLAN':
return {
...state,
[action.id]: {
...state[action.id],
...action.plan
}
};
default:
if (action.entities && action.entities.plans) {
return merge({}, state, action.entities.plans);
}
return state;
}
}
const entities = combineReducers({
plans,
exercises
});
const currentPlans = (state = [], action) {
switch (action.type) {
case 'CREATE_PLAN':
return [...state, action.id];
default:
return state;
}
}
const reducer = combineReducers({
entities,
currentPlans
});
๊ทธ๋์ ์ฌ๊ธฐ์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์์ต๋๊น? ๋จผ์ ์ํ๊ฐ ์ ๊ทํ๋์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๋ค๋ฅธ ์ํฐํฐ ์์ ์ํฐํฐ๊ฐ ์์ต๋๋ค. ๋์ ID๋ก ์๋ก๋ฅผ ์ฐธ์กฐํฉ๋๋ค. ๋ฐ๋ผ์ ์ผ๋ถ ๊ฐ์ฒด๊ฐ ๋ณ๊ฒฝ ๋ ๋๋ง๋ค ์ ๋ฐ์ดํธํด์ผํ๋ ๋จ์ผ ์์น ๋ง ์์ต๋๋ค.
๋์งธ, plans
๊ฐ์๊ธฐ์ ์ ์ ํ ์ํฐํฐ๋ฅผ ์ถ๊ฐํ๊ณ currentPlans
๊ฐ์๊ธฐ์ ID๋ฅผ ์ถ๊ฐํ์ฌ CREATE_PLAN
์ ์ด๋ป๊ฒ ๋ฐ์ํ๋์ง ํ์ธํฉ๋๋ค. ์ด๊ฑด ์ค์ํ๋ค. ๋ ๋ณต์กํ ์ฑ์์๋ ๊ด๊ณ๊ฐ์์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด plans
๊ฐ์๊ธฐ๋ ๊ณํ ๋ด์ ๋ฐฐ์ด์ ์ ID๋ฅผ ์ถ๊ฐํ์ฌ ๋์ผํ ๋ฐฉ์์ผ๋ก ADD_EXERCISE_TO_PLAN
๋ฅผ ์ฒ๋ฆฌ ํ ์ โโ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ์์ฒด๊ฐ ์
๋ฐ์ดํธ๋๋ฉด _ID๊ฐ ๋ณ๊ฒฝ๋์ง ์์์ผ๋ฏ๋ก plans
๊ฐ์๊ธฐ๊ฐ์ด๋ฅผ ์ ํ์๊ฐ ์์ต๋๋ค _.
์
์งธ, ์ํฐํฐ ๊ฐ์๊ธฐ ( plans
๋ฐ exercises
)์๋ action.entities
๊ฐ์ํ๋ ํน์ ์ ์ด ์์ต๋๋ค. ์ด๊ฒ์ ์ฐ๋ฆฌ๊ฐ ๋ฐ์ํ๊ธฐ ์ํด ๋ชจ๋ ์ํฐํฐ๋ฅผ ์
๋ฐ์ดํธํ๊ณ ์ํ๋ "์๋ ค์ง ์ง์ค"์ ๊ฐ์ง ์๋ฒ ์๋ต์ด์๋ ๊ฒฝ์ฐ์
๋๋ค. ์์
์ ์ ๋ฌํ๊ธฐ ์ ์ ์ด๋ฌํ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ค๋นํ๋ ค๋ฉด normalizr ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. Redux repo์ "real world"์์ ์์ ์ฌ์ฉ ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก ์ํฐํฐ ๊ฐ์๊ธฐ๊ฐ ์ด๋ป๊ฒ ์ ์ฌํ ์ง ํ์ธํ์ญ์์ค. ์ด๋ฅผ ์์ฑํ๋ ํจ์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ๋ด ๋๋ต์ ๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ต๋๋ค. ๋๋ก๋ ๋ ๋ง์ ์ ์ฐ์ฑ์ ์ํ๊ณ ๋๋ก๋ ๋ ์ ์ ์์ฉ๊ตฌ๋ฅผ ์ํฉ๋๋ค. ์ ์ฌํ ๊ฐ์๊ธฐ๋ฅผ ์์ฑํ๋ ์๋ฅผ ๋ณด๋ ค๋ฉด "์ค์ "๊ฐ์๊ธฐ ์์ ์์ ํ์ด์ง ๋งค๊น ์ฝ๋๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์, ๊ทธ๋ฆฌ๊ณ { ...a, ...b }
๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ต๋๋ค. Babel 2 ๋จ๊ณ์์ ES7 ์ ์์ผ๋ก ํ์ฑํ๋ฉ๋๋ค. "๊ฐ์ฒด ํ์ฐ ์ฐ์ฐ์"๋ผ๊ณ ํ๋ฉฐ Object.assign({}, a, b)
๋ฅผ ์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ Lodash๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค (์๋ฅผ ๋ค์ด merge({}, a, b}
๋ ์ ํํ์ง๋ง merge(a, b)
์ ์ฌ๋ฐ๋ฅด์ง ์์), updeep , react-addons-update ๋๋ ๋ค๋ฅธ ๊ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ฌ์ธต ์
๋ฐ์ดํธ๊ฐ ํ์ํ ๊ฒฝ์ฐ ์ํ ํธ๋ฆฌ๊ฐ ์ถฉ๋ถํ ํํํ์ง ์๊ณ ๊ธฐ๋ฅ ๊ตฌ์ฑ์ ์ถฉ๋ถํ ํ์ฉํ์ง ์์ ์์ ์๋ฏธํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ์๋ :
case 'UPDATE_PLAN':
return {
...state,
plans: [
...state.plans.slice(0, action.idx),
Object.assign({}, state.plans[action.idx], action.plan),
...state.plans.slice(action.idx + 1)
]
};
๋ค์๊ณผ ๊ฐ์ด ์ธ ์ ์์ต๋๋ค.
const plan = (state = {}, action) => {
switch (action.type) {
case 'UPDATE_PLAN':
return Object.assign({}, state, action.plan);
default:
return state;
}
}
const plans = (state = [], action) => {
if (typeof action.idx === 'undefined') {
return state;
}
return [
...state.slice(0, action.idx),
plan(state[action.idx], action),
...state.slice(action.idx + 1)
];
};
// somewhere
case 'UPDATE_PLAN':
return {
...state,
plans: plans(state.plans, action)
};
์ด๊ฒ์ ์กฐ๋ฆฌ๋ฒ์ผ๋ก ๋ฐ๊พธ๋ฉด ์ข์ ๊ฒ์ ๋๋ค.
์ด์์ ์ผ๋ก ์ฐ๋ฆฌ๋ ๊ฐํ ๋ณด๋ ์์ ๋ฅผ ์ํฉ๋๋ค.
"๋ ์ธ"๋ด๋ถ์ "์นด๋"๊ฐ์์ ์ ์๊ธฐ ๋๋ฌธ์ ์ค์ฒฉ ๋ ์ํฐํฐ์ ์ ํฉํฉ๋๋ค.
@ andre0799 ๋๋ Immutable.js๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค))
์ด์์ ์ผ๋ก ์ฐ๋ฆฌ๋ ๊ฐํ ๋ณด๋ ์์ ๋ฅผ ์ํฉ๋๋ค.
๋๋ ํ๋๋ฅผ ์ผ๋ค. ์๋ง๋ ๋น์ ์ ๊ทธ๊ฒ์ ํฌํฌํ๊ณ ์ํ๋๋๋ก ์กฐ์ ํ ์ ์์ต๋๋ค.
Immutable.js๊ฐ ํญ์ ์ข์ ํด๊ฒฐ์ฑ ์ ์๋๋๋ค. ๋ณ๊ฒฝ ํ ๋ ธ๋์์ ์์ํ์ฌ ์ํ์ ๋ชจ๋ ์์ ๋ ธ๋์ ํด์๋ฅผ ๋ค์ ๊ณ์ฐํ๋ฉฐ ์ด๋ ํน์ ๊ฒฝ์ฐ์ ๋ณ๋ชฉ ํ์์ด๋ฉ๋๋ค (์ด๊ฒ์ ๋งค์ฐ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ๊ฐ ์๋๋๋ค). ๋ฐ๋ผ์ ์ด์์ ์ผ๋ก๋ Immutable.js๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ํตํฉํ๊ธฐ ์ ์ ๋ช ๊ฐ์ง ๋ฒค์น ๋งํฌ๋ฅผ ๋ง๋ค์ด์ผํฉ๋๋ค.
๊ทํ์ ๋ต๋ณ, ํ๋ฅญํ ์ค๋ช ์ ๋ํด @gaearon ์๊ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค!
๋ฐ๋ผ์ CREATE_PLAN
์ ์ํํ๋ฉด ์๋์ผ๋ก ๊ธฐ๋ณธ ์ด๋์ ์์ฑํ๊ณ ์ถ๊ฐํด์ผํฉ๋๋ค. ์ด์ ๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผํฉ๋๊น? ๊ทธ๋ฐ ๋ค์ 3 ๊ฐ์ ์์
์ ์ฐ์์ผ๋ก ํธ์ถํด์ผํฉ๋๊น? CREATE_PLAN, CREATE_EXERCISE, ADD_EXERCISE_TO_PLAN
๊ฒฝ์ฐ ์ด๋์์ ์ ํ๋ฅผํด์ผํฉ๋๊น?
๋ฐ๋ผ์ CREATE_PLAN์ ์ํ ํ ๋ ๊ธฐ๋ณธ ์ด๋์ ์๋์ผ๋ก ์์ฑํ๊ณ ์ถ๊ฐํด์ผํฉ๋๋ค. ์ด์ ๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผํฉ๋๊น?
์ผ๋ฐ์ ์ผ๋ก ๋์ผํ ์์ ์ ์ฒ๋ฆฌํ๋ ๋ง์ ๋ฆฌ๋์๋ฅผ ์ ํธํ์ง๋ง ๊ด๊ณ๊ฐ์๋ ์ํฐํฐ์๋ ๋๋ฌด ๋ณต์กํด์ง ์ ์์ต๋๋ค. ์ค์ ๋ก ๋๋ ์ด๊ฒ๋ค์ ๋ณ๊ฐ์ ํ๋์ผ๋ก ๋ชจ๋ธ๋ง ํ ๊ฒ์ ์ ์ํ๊ณ ์์ต๋๋ค.
Redux Thunk ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋ค ํธ์ถํ๋ ์ก์ ์์ฑ์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
function createPlan(title) {
return dispatch => {
const planId = uuid();
const exerciseId = uuid();
dispatch({
type: 'CREATE_EXERCISE',
id: exerciseId,
exercise: {
id: exerciseId,
title: 'Default'
}
});
dispatch({
type: 'CREATE_PLAN',
id: planId,
plan: {
id: planId,
exercises: [exerciseId],
title
}
});
};
}
๊ทธ๋ฐ ๋ค์ Redux Thunk ๋ฏธ๋ค์จ์ด๋ฅผ ์ ์ฉํ๋ฉด ์ ์์ ์ผ๋ก ํธ์ถ ํ ์ ์์ต๋๋ค.
store.dispatch(createPlan(title));
๋ฐฑ์๋ ์ด๋๊ฐ์ ๊ทธ๋ฌํ ๊ด๊ณ (๊ฒ์๋ฌผ, ์์ฑ์, ํ๊ทธ, ์ฒจ๋ถ ํ์ผ ๋ฑ)๊ฐ์๋ ๊ฒ์๋ฌผ ํธ์ง๊ธฐ๊ฐ ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
currentPlans
ํค ๋ฐฐ์ด๊ณผ ์ ์ฌํ currentPosts
ํ์ํ๋ ค๋ฉด ์ด๋ป๊ฒํด์ผํฉ๋๊น? currentPosts
๊ฐ ํค๋ฅผ mapStateToProps
ํจ์์ entities.posts
์์๋ ํด๋น ๊ฐ์ฒด์ ๋งคํํด์ผํฉ๋๊น? currentPosts
์ ๋ ฌ
์ด ๋ชจ๋ ๊ฒ์ด ๊ฐ์๊ธฐ ๊ตฌ์ฑ์ ์ํฉ๋๊น?
์ฌ๊ธฐ ๋ญ๊ฐ ๋น ์ก์ด์ ...
์๋ ์ง๋ฌธ๊ณผ ๊ด๋ จํ์ฌ React Immutability Helpers ๊ฐ ๊ทธ ๋ชฉ์ ์ ์ํด ๋ง๋ค์ด ์ก๋ค๊ณ ์๊ฐํฉ๋๋ค
currentPlans ํค ๋ฐฐ์ด๊ณผ ์ ์ฌํ currentPosts๋ฅผ ํ์ํ๋ ค๋ฉด ์ด๋ป๊ฒํด์ผํฉ๋๊น? currentPosts์ ๊ฐ ํค๋ฅผ mapStateToProps ํจ์์ entity.posts์์๋ ํด๋น ๊ฐ์ฒด์ ๋งคํํด์ผํฉ๋๊น? currentPosts๋ฅผ ์ ๋ ฌํ๋ ๊ฒ์ ์ด๋ป์ต๋๊น?
๋ง์ต๋๋ค. ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ ํ ๋ ๋ชจ๋ ์์ ์ ์ํํฉ๋๋ค. Redux ์ ์ฅ์์ ํจ๊ป ์ ๊ณต๋๋ "์ฅ๋ฐ๊ตฌ๋"๋ฐ "์ค์ ์ธ๊ณ"์์ ๋ฅผ ์ฐธ์กฐํ์ญ์์ค.
๊ฐ์ฌํฉ๋๋ค. ๋ฌธ์์์ ํ์ ๋ฐ์ดํฐ ๊ณ์ฐ ์ ์ฝ์ ํ ์ด๋ฏธ ์์ด๋์ด๋ฅผ ์ป๊ธฐ ์์ํ์ต๋๋ค.
์ด ์์ ๋ฅผ ๋ค์ ํ์ธํ๊ฒ ์ต๋๋ค. ์ฒ์ ์ฝ์์ ๋ ๋ฌด์จ ์ผ์ด ๋ฒ์ด ์ก๋์ง ์ดํดํ์ง ๋ชปํ์ ๊ฒ์ ๋๋ค.
๋ฟก ๋นต๋จ
๋ชจ๋ connect()
ed ๊ตฌ์ฑ ์์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก dispatch
๊ฐ prop์ผ๋ก ์ฝ์
๋ฉ๋๋ค.
this.props.dispatch(createPlan(title));
์ด ์ค๋ ๋์ ๊ด๋ จ์ด์๋ ์ฌ์ฉ ์ง๋ฌธ์ ๋๋ค. ์ด์ ๋ํ ์์ ๋ฅผ ์ฐธ์กฐํ๊ฑฐ๋ StackOverflow ์ง๋ฌธ์ ๋ง๋๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ ๋ Dan์ด ๋ฐ์ดํฐ๋ฅผ ์ ๊ทํํ๊ณ ๊ฐ๋ฅํ ํ ์ํ ๊ตฌ์กฐ๋ฅผ ํํํํ๋ ๊ฒ์ ๋์ํฉ๋๋ค. ๊ทธ๊ฒ์ ๋์๊ฒ ์ฝ๊ฐ์ ๋ํต์ ๋์ด ์ฃผ์์ ๊ฒ์ด๋ฏ๋ก ๋ฌธ์์ ๋ ์ํผ / ๋ชจ๋ฒ ์ฌ๋ก๋ก ๋ฃ์ ์ ์์ต๋๋ค.
๋ด ์ํ์์ ์ฝ๊ฐ์ ๊น์ด๋ฅผ ๊ฐ๋ ์ค์๋ฅผํ๊ธฐ ๋๋ฌธ์ Redux๋ฅผ ์ฌ์ฉํ์ฌ ๊น์ ์ํ๋ฅผ ๊ฐ์กฐํ๊ณ ๊ด๋ฆฌํ๋ ๋ฐ ๋์์ด๋๋๋ก์ด lib๋ฅผ ๋ง๋ค์์ต๋๋ค. https://github.com/baptistemanson/immutable-path
์๋ง ๋ชจ๋ ๊ฒ์ด ์๋ชป๋์์ง๋ง ๊ทํ์ ์๊ฒฌ์ ๊ด์ฌ์ด ์์ต๋๋ค. ๋๊ตฐ๊ฐ๋ฅผ ๋์ธ ์ ์๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ด๊ฒ์ ๋์์ด๋์์ต๋๋ค. ๋ชจ๋์๊ฒ ๊ฐ์ฌํฉ๋๋ค.
์ค์ ์์ ์ ๋์ผํ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ณํ์ ์ด๋์ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์
๋๊น? ์ด๋์ ์ถ๊ฐํ๋ฉด planId
ํ๋์ ํจ๊ป ์๋ก ์์ฑ ๋ ์ด๋ ํญ๋ชฉ์ด ๋ฐํ๋์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. ๊ณํ์ ๋ํ ๊ฐ์๊ธฐ๋ฅผ ์์ฑํ์ง ์๊ณ ํด๋น ๊ณํ์ ์ ์ด๋์ ์ถ๊ฐํ๊ณ ํน๋ณํ CREATE_EXERCISE
์์
์๋ค์ ์ ์์ต๋๊น?
์ฌ๊ธฐ์ ํ๋ฅญํ ํ ๋ก ๊ณผ ์ ๋ณด๊ฐ ์์ต๋๋ค. ๋ด ํ๋ก์ ํธ์ normalizr์ ์ฌ์ฉํ๊ณ ์ถ์ง๋ง ์ ๋ฐ์ดํธ ๋ ๋ฐ์ดํฐ๋ฅผ ์๊ฒฉ ์๋ฒ์ ๋ค์ ์ ์ฅํ๋ ๊ฒ๊ณผ ๊ด๋ จํ์ฌ ์ง๋ฌธ์ด ์์ต๋๋ค. ์ฃผ๋ก, ์ ๋ฐ์ดํธ ํ ์ ๊ทํ ๋ ๋ชจ์์ ์๊ฒฉ API์์ ์ ๊ณตํ๋ ์ค์ฒฉ ๋ชจ์์ผ๋ก ๋ ๋๋ฆฌ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด ์์ต๋๊น? ์ด๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ณ๊ฒฝ์ ์ํํ๊ณ ์ ๋ฐ์ดํธ ์์ฒญ์ ํํ๋ฅผ ์ ์ด ํ ์์๋ ์๊ฒฉ API๋ก ํผ๋๋ฐฑํด์ผ ํ ๋ ์ค์ํฉ๋๋ค.
์ : ํด๋ผ์ด์ธํธ๊ฐ ์ค์ฒฉ ๋ ์ด๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด-> ํด๋ผ์ด์ธํธ๊ฐ์ด๋ฅผ ์ ๊ทํํ์ฌ redux์ ์ ์ฅ-> ์ฌ์ฉ์๊ฐ ํด๋ผ์ด์ธํธ ์ธก์์ ์ ๊ทํ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝ-> ์ฌ์ฉ์๊ฐ ์ ์ฅ์ ํด๋ฆญ-> ํด๋ผ์ด์ธํธ ์ฑ์ด ์ ๋ฐ์ดํธ ๋ ์ ๊ทํ ๋ ๋ฐ์ดํฐ๋ฅผ ์ค์ฒฉ ๋ ํํ๋ก ๋ค์ ๋ณํ ๊ทธ๋์ ๊ทธ๊ฒ์ ์๊ฒฉ ์๋ฒ์ ์ ์ถํ ์ ์์ต๋๋ค -> ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ ์ถ
normalizr์ ์ฌ์ฉํ๋ค๋ฉด ๊ตต์ ๋จ๊ณ์ ๋ํ ์ฌ์ฉ์ ์ง์ ๋ณํ๊ธฐ๋ฅผ ์์ฑํด์ผํฉ๋๊น? ์๋๋ฉด์ด๋ฅผ ์ํด ๊ถ์ฅ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋ ๋์ฐ๋ฏธ ๋ฉ์๋๊ฐ ์์ต๋๊น? ๋ชจ๋ ๊ถ์ฅ ์ฌํญ์ ๋ง์ด ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
๊ฐ์ฌ
https://github.com/gpbl/denormalizr ์ด๋ผ๋ ๊ฒ์ด ์์ง๋ง normalizr ์ ๋ฐ์ดํธ๋ฅผ ์ผ๋ง๋ ๋ฐ์ ํ๊ฒ ์ถ์ ํ๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ๋ด๊ฐ ์์ ํ ์ฑ์ ๋ํด ๋ช ์๊ฐ ๋ง์ normalizr์ ์์ฑํ์ต๋๋ค. ํฌํฌํ๊ณ ๋น์ ๊ท ํ๋ฅผ ์ถ๊ฐ ํ ์ ์์ต๋๋ค ๐.
๋ฉ์ง๋ค. ๋น์ ๊ท ํ๋ฅผ ์ดํด๋ณด๊ณ ๋ฌด์ธ๊ฐ๊ฐ ์์ผ๋ฉด ํ๋ก์ ํธ์ ๋ค์ ๊ธฐ์ฌํ ๊ฒ์ ๋๋ค. ๋ช ์๊ฐ ๋์ ๋๋จํ ์ผ์ํ์ต๋๋ค ;-) ๋ค์ ์ฐ๋ฝํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
๋๋ ๊น์ด ์ค์ฒฉ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ๊ณผ ๊ฐ์ ์ํฉ์ ์์ง๋ง immutable.js๋ฅผ ์ฌ์ฉํ์ฌ ์๋ํ๋ ์๋ฃจ์ ์ ์ฐพ์์ต๋๋ค.
์ฌ๊ธฐ์์ ์๋ฃจ์ ์ ๋ํ ํผ๋๋ฐฑ์ ์์ฒญํ StackOverflow ๊ฒ์๋ฌผ์ ๋งํฌํด๋ ๊ด์ฐฎ์ต๋๊น?
์ง๊ธ ๋งํฌํ๊ณ ์์ต๋๋ค. ๋ด ๊ฒ์๋ฌผ์ ์ญ์ ํ๊ฑฐ๋ ์ฌ๊ธฐ์ ๋งํฌํ๋ ๊ฒ์ด ๋ถ์ ์ ํ๋ค๊ณ ๋ง ํด์ฃผ์ธ์ :
http://stackoverflow.com/questions/37171203/manipulating-data-in-nested-arrays-in-redux-with-immutable-js
์ด ์ ๊ทผ ๋ฐฉ์์ด ๊ณผ๊ฑฐ์ ๊ถ์ฅ๋๋ ๊ฒ์ ๋ณด์์ต๋๋ค. ๊ทธ๋ฌ๋์ด ๋ฐฉ๋ฒ์ ์ค์ฒฉ ๋ ๊ฐ์ฒด๋ฅผ ์ ๊ฑฐํด์ผํ๋ ๊ฒฝ์ฐ ์ ๋๋ก ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด ๊ฒฝ์ฐ ๋ฆฌ๋์๋ ๊ฐ์ฒด ์์ฒด๋ฅผ ์ ๊ฑฐํ๊ธฐ ์ ์ ๊ฐ์ฒด์ ๋ํ ๋ชจ๋ ์ฐธ์กฐ๋ฅผ ์ดํด๋ณด๊ณ ์ด๋ฅผ ์ ๊ฑฐํด์ผํฉ๋๋ค.์ด ์์ ์ O (n)์ด๋ฉ๋๋ค. ๋๊ตฌ๋ ์ง ๋น์ทํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ํด๊ฒฐ ํ์ต๋๊น?
@ariofrio : ์ ...
๋น์ ์ด ๊ฐ์ง๊ณ ์๋ ํน์ ํ ์ฐ๋ ค ์ฌํญ์ด ์์ต๋๊น, ์๋๋ฉด ๋น์ ์ด ๋ค๋ฃจ๊ณ ์๋ ์ฑ๊ฐ์ ์๋๋ฆฌ์ค๊ฐ ์์ต๋๊น?
์ ๊ฐ ์๋ฏธํ๋ ๋ฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ํ์ฌ ์ํ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
{
entities: {
plans: {
1: {title: 'A', exercises: [1, 2, 3]},
2: {title: 'B', exercises: [5, 6]}
},
exercises: {
1: {title: 'exe1'},
2: {title: 'exe2'},
3: {title: 'exe3'}
5: {title: 'exe5'}
6: {title: 'exe6'}
}
},
currentPlans: [1, 2]
}
์ด ์์์ ๊ฐ ์ด๋์ ํ๋์ ๊ณํ์์๋ง ์ฐธ์กฐ ํ ์ ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ "์ด๋ ์ ๊ฑฐ"๋ฅผ ํด๋ฆญํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฉ์์ง๊ฐ ํ์ ๋ ์ ์์ต๋๋ค.
{type: "REMOVE_EXERCISE", payload: 2}
๊ทธ๋ฌ๋ ์ด๊ฒ์ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํํ๋ ค๋ฉด ๋งค๋ฌ๋ฆฐ ์ฐธ์กฐ๋ฅผ ํผํ๊ธฐ ์ํด ๋ชจ๋ ๊ณํ์ ๋ฐ๋ณต ํ ๋ค์ ๊ฐ ๊ณํ ๋ด์ ๋ชจ๋ ์ฐ์ต์ ๋ฐ๋ณตํ์ฌ id 2๋ก ์ฐ์ต์ ์ฐธ์กฐํ๋ ํ๋๋ฅผ ์ฐพ์์ผํฉ๋๋ค. ์ด๊ฒ์ด ์ ๊ฐ ๊ฑฑ์ ํ๋ O (n) ์์ ์ ๋๋ค.
์ด๋ฅผ ํผํ๋ ๋ฐฉ๋ฒ์ REMOVE_EXERCISE์ ํ์ด๋ก๋์ ๊ณํ ID๋ฅผ ํฌํจํ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋์ด ์์ ์์ ๊ตฌ์กฐ ์ค์ฒฉ์ ์ฌ์ฉํ๋ ๊ฒ๋ณด๋ค ์ด์ ์ด ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ๋์ ์ค์ฒฉ ์ํ๋ฅผ ์ฌ์ฉํ๋ฉด ์ํ๋ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค.
{
plans: [
{title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
{title: 'B', exercises: [{title: 'exe5'}, {title: 'exe6'}]}
]
}
์ด๋์ ์ ๊ฑฐํ๋ผ๋ ๋ฉ์์ง๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
{type: "REMOVE_EXERCISE", payload: {plan_index: 0, exercise_index: 1}}
๋ช ๊ฐ์ง ์๊ฐ :
{id, planId, exerciseId}
ํธ๋ฆฌํ๋ ์ด ํฌํจ ๋ "PlanExercise" "ํ
์ด๋ธ"์ด ์์ ์ ์์ต๋๋ค. ํ์คํ O (n) ์ค์บ์ด์ง๋ง ๊ฐ๋จํฉ๋๋ค.๊ถ๊ทน์ ์ผ๋ก ์ค์ฒฉ ๋ฐ ์ ๊ทํ ๋ ์ํ๋ ๋ชจ๋ ๊ท์น์ ๋๋ค. Redux์ ํจ๊ป ์ ๊ทํ ๋ ์ํ๋ฅผ ์ฌ์ฉํ๋ ๋ฐ์๋ ์ข์ ์ด์ ๊ฐ ์์ผ๋ฉฐ ์ํ๋ฅผ ์ ๊ทํ ๋ ์ํ๋ก ์ ์งํด์ผํ๋ ์ข์ ์ด์ ๊ฐ์์ ์ ์์ต๋๋ค. ๋น์ ์๊ฒ ๋ง๋ ๊ฒ์ ์ ํํ์ญ์์ค :)
๋ด ์๋ฃจ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
function deepCombinReducer(parentReducer, subReducer) {
return function (state = parentReducer(void(0) /* get parent reducer initial state */, {}) {
let finalState = {...state};
for (var k in subReducer) {
finalState[k] = subReducer(state[k], action);
}
return parentReducer(finalState, action);
};
}
const parentReducer = function(state = {}, action) {
return state;
}
const subReducer = function(state = [], action) {
state = Immutable.fromJS(state).toJS();
switch(action.type) {
case 'ADD':
state.push(action.sub);
return state;
default:
return state;
}
}
export default combineReducers({
parent: deepCombinReducer(parentReducer, {
sub: subReducer
})
})
๊ทธ๋ฐ ๋ค์ ๋ค์๊ณผ ๊ฐ์ ์์ ์ ์ป์ ์ ์์ต๋๋ค.
{
parent: {
sub: []
}
}
dispatch({
type: 'ADD',
sub: '123'
});
// the store will change to:
{
parent: {
sub: ['123']
}
}
@smashercosmo immutable.js
๊น์ ์ค์ฒฉ ์ํ? ์ด๋ป๊ฒ ๊ถ๊ธํด
@gaearon
์ฐ๋ฆฌ๋ ๋ค๋ฅธ ์ํฐํฐ ์์ ์ํฐํฐ๊ฐ ์์ต๋๋ค.
์ดํด๊ฐ ์ ๋ผ์. ์ฌ๊ธฐ์๋ ์ต์ํ ์ธ ๊ฐ์ง ์์ค์ ์ค์ฒฉ์ด ์์ต๋๋ค.
{
entities: {
plans: {
1: {title: 'A', exercises: [1, 2, 3]},
2: {title: 'B', exercises: [5, 1, 2]}
},
exercises: {
1: {title: 'exe1'},
2: {title: 'exe2'},
3: {title: 'exe3'}
}
},
currentPlans: [1, 2]
}
entities.plans[1] - three levels
entities.exercises[1] - three levels
์ด๊ฒ์ ์ค์ฒฉ๋์ง ์์ ๊ฐ์ฒด์ ๋๋ค. ํ ์์ค ๋ง.
{
plans: [1,2, 3],
exercises: [1,2,3],
'so forth': [1,2,3]
}
@wzup : ์ฐธ๊ณ ๋ก Dan์ ์์ฆ Redux ๋ฌธ์ ๋ฅผ ๋ณด๋ ๋ฐ ๋ง์ ์๊ฐ์ ์๋นํ์ง ์์ต๋๋ค. ๊ทธ๋ React์์ ์์ ํ๋ ๋ฐ ์ถฉ๋ถํ ์๊ฐ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
์ฌ๊ธฐ์ "์ค์ฒฉ"์ ์๋ฏธ๋ ์ด์ ์ค๋ ๋์ ๋ค์ ์์ ์ ๊ฐ์ด ๋ฐ์ดํฐ ์์ฒด๊ฐ ์ค์ฒฉ ๋ ๊ฒฝ์ฐ์ ๋๋ค.
{
plans: [
{title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
{title: 'B', exercises: [{title: 'exe5'}, {title: 'exe6'}]}
]
}
์ด ์์ ์์ ์ฐ์ต "exe6"์ ์ก์ธ์คํ๋ ์ ์ผํ ๋ฐฉ๋ฒ์ plans[1].exercises[2]
์ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ํํค์น๋ ๊ฒ์
๋๋ค.
@tmonte ์ ์ง๋ฌธ์ ๊ด์ฌ์ด ์์ต๋๋ค.
์ค์ ์์ ์ ๋์ผํ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ณํ์ ์ด๋์ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ ๋๊น? ์ด๋์ ์ถ๊ฐํ๋ฉด planId ํ๋์ ํจ๊ป ์๋ก ์์ฑ ๋ ์ด๋ ํญ๋ชฉ์ด ๋ฐํ๋์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. ๊ณํ์ ๋ํ ๊ฐ์๊ธฐ๋ฅผ ์์ฑํ์ง ์๊ณ ํด๋น ๊ณํ์ ์ ์ฐ์ต์ ์ถ๊ฐํ๊ณ ๊ตฌ์ฒด์ ์ผ๋ก CREATE_EXERCISE ์์ ์๋ค์ ์ ์์ต๋๊น?
์ํฐํฐ๊ฐ ๋ง์ ๊ฒฝ์ฐ ๊ฐ ์ํฐํฐ์ ๋ํด ํ๋์ ๊ฐ์๊ธฐ๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ด๋ ค์ธ ์ ์์ง๋ง์ด ์ ๊ทผ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ ์ ์์ต๋๋ค. ์ง๊ธ๊น์ง ํด๊ฒฐ์ฑ ์ ์ฐพ์ง ๋ชปํ์ต๋๋ค.
๋ ๋ง์ ์ ์ฐ์ฑ์ ์ํด merge
๋์ mergeWith
์ฌ์ฉํฉ๋๋ค.
import mergeWith from 'lodash/mergeWith';
// Updates an entity cache in response to any action with `entities`.
function entities(state = {}, action) {
// Here where we STORE or UPDATE one or many entities
// So check if the action contains the format we will manage
// wich is `payload.entities`
if (action.payload && action.payload.entities) {
// if the entity is already in the store replace
// it with the new one and do not merge. Why?
// Assuming we have this product in the store:
//
// products: {
// 1: {
// id: 1,
// name: 'Awesome product name',
// rateCategory: 1234
// }
// }
//
// We will updated with
// products: {
// 1: {
// id: 1,
// name: 'Awesome product name',
// }
// }
//
// The result if we were using `lodash/merge`
// notice the rate `rateCategory` hasn't changed:
// products: {
// 1: {
// id: 1,
// name: 'Awesome product name',
// rateCategory: 1234
// }
// }
// for this particular use case it's safer to use
// `lodash/mergeWith` and skip the merge
return mergeWith({}, state, action.payload.entities, (oldD, newD) => {
if (oldD && oldD.id && oldD.id === newD.id) {
return newD;
}
return undefined;
});
}
// Here you could register other handlers to manipulate
// the entities
switch (action.type) {
case ActionTypes.SOME_ACTION:
// do something;
default:
return state;
}
}
const rootReducer = combineReducers({
entities,
});
export default rootReducer;
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์, ๋ฐ์ดํฐ๋ฅผ ์ ๊ทํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ด๋ ๊ฒํ๋ฉด "๊น์ด"๊ฐ ํ์๊ฐ ์์ต๋๋ค. ๋ชจ๋ ์ํฐํฐ๊ฐ ๋์ผํ ์์ค์ ์์ต๋๋ค.
๊ทธ๋์ ๋น์ ์ ์ํ๋
๊ฐ์๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ์ ์์ต๋๋ค.
๊ทธ๋์ ์ฌ๊ธฐ์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์์ต๋๊น? ๋จผ์ ์ํ๊ฐ ์ ๊ทํ๋์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๋ค๋ฅธ ์ํฐํฐ ์์ ์ํฐํฐ๊ฐ ์์ต๋๋ค. ๋์ ID๋ก ์๋ก๋ฅผ ์ฐธ์กฐํฉ๋๋ค. ๋ฐ๋ผ์ ์ผ๋ถ ๊ฐ์ฒด๊ฐ ๋ณ๊ฒฝ ๋ ๋๋ง๋ค ์ ๋ฐ์ดํธํด์ผํ๋ ๋จ์ผ ์์น ๋ง ์์ต๋๋ค.
๋์งธ,
plans
๊ฐ์๊ธฐ์ ์ ์ ํ ์ํฐํฐ๋ฅผ ์ถ๊ฐํ๊ณcurrentPlans
๊ฐ์๊ธฐ์ ID๋ฅผ ์ถ๊ฐํ์ฌCREATE_PLAN
์ ์ด๋ป๊ฒ ๋ฐ์ํ๋์ง ํ์ธํฉ๋๋ค. ์ด๊ฑด ์ค์ํ๋ค. ๋ ๋ณต์กํ ์ฑ์์๋ ๊ด๊ณ๊ฐ์์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ดplans
๊ฐ์๊ธฐ๋ ๊ณํ ๋ด์ ๋ฐฐ์ด์ ์ ID๋ฅผ ์ถ๊ฐํ์ฌ ๋์ผํ ๋ฐฉ์์ผ๋กADD_EXERCISE_TO_PLAN
๋ฅผ ์ฒ๋ฆฌ ํ ์ โโ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ์์ฒด๊ฐ ์ ๋ฐ์ดํธ๋๋ฉด _ID๊ฐ ๋ณ๊ฒฝ๋์ง ์์์ผ๋ฏ๋กplans
๊ฐ์๊ธฐ๊ฐ์ด๋ฅผ ์ ํ์๊ฐ ์์ต๋๋ค _.์ ์งธ, ์ํฐํฐ ๊ฐ์๊ธฐ (
plans
๋ฐexercises
)์๋action.entities
๊ฐ์ํ๋ ํน์ ์ ์ด ์์ต๋๋ค. ์ด๊ฒ์ ์ฐ๋ฆฌ๊ฐ ๋ฐ์ํ๊ธฐ ์ํด ๋ชจ๋ ์ํฐํฐ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ ์ํ๋ "์๋ ค์ง ์ง์ค"์ ๊ฐ์ง ์๋ฒ ์๋ต์ด์๋ ๊ฒฝ์ฐ์ ๋๋ค. ์์ ์ ์ ๋ฌํ๊ธฐ ์ ์ ์ด๋ฌํ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ค๋นํ๋ ค๋ฉด normalizr ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. Redux repo์ "real world"์์ ์์ ์ฌ์ฉ ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.๋ง์ง๋ง์ผ๋ก ์ํฐํฐ ๊ฐ์๊ธฐ๊ฐ ์ด๋ป๊ฒ ์ ์ฌํ ์ง ํ์ธํ์ญ์์ค. ์ด๋ฅผ ์์ฑํ๋ ํจ์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ๋ด ๋๋ต์ ๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ต๋๋ค. ๋๋ก๋ ๋ ๋ง์ ์ ์ฐ์ฑ์ ์ํ๊ณ ๋๋ก๋ ๋ ์ ์ ์์ฉ๊ตฌ๋ฅผ ์ํฉ๋๋ค. ์ ์ฌํ ๊ฐ์๊ธฐ๋ฅผ ์์ฑํ๋ ์๋ฅผ ๋ณด๋ ค๋ฉด "์ค์ "๊ฐ์๊ธฐ ์์ ์์ ํ์ด์ง ๋งค๊น ์ฝ๋๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์, ๊ทธ๋ฆฌ๊ณ
{ ...a, ...b }
๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ต๋๋ค. Babel 2 ๋จ๊ณ์์ ES7 ์ ์์ผ๋ก ํ์ฑํ๋ฉ๋๋ค. "๊ฐ์ฒด ํ์ฐ ์ฐ์ฐ์"๋ผ๊ณ ํ๋ฉฐObject.assign({}, a, b)
๋ฅผ ์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค.๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ Lodash๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค (์๋ฅผ ๋ค์ด
merge({}, a, b}
๋ ์ ํํ์ง๋งmerge(a, b)
์ ์ฌ๋ฐ๋ฅด์ง ์์), updeep , react-addons-update ๋๋ ๋ค๋ฅธ ๊ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ฌ์ธต ์ ๋ฐ์ดํธ๊ฐ ํ์ํ ๊ฒฝ์ฐ ์ํ ํธ๋ฆฌ๊ฐ ์ถฉ๋ถํ ํํํ์ง ์๊ณ ๊ธฐ๋ฅ ๊ตฌ์ฑ์ ์ถฉ๋ถํ ํ์ฉํ์ง ์์ ์์ ์๋ฏธํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ์๋ :๋ค์๊ณผ ๊ฐ์ด ์ธ ์ ์์ต๋๋ค.