export default function App() {
const [number, setNumber] = useState(1);
return (
<div id="container">
<h1>Root : {number}</h1>
<div id="grid">
<Left1 number={number}></Left1> // state를 props로 전달
<Right1 setNumber={setNumber}></Right1> // setState를 props로 전달
</div>
</div>
);
}
function Left1(props) {
return (
<div>
<h1>Left1</h1>
<Left2 number={props.number}></Left2> // props 전달
</div>
);
}
function Left2(props) {
return (
<div>
<h1>Left2</h1>
<Left3 number={props.number}></Left3> // props 전달
</div>
);
}
function Left3(props) {
return (
<div>
<h1>Left3 : {props.number}</h1> // 최종 전달 된 props
</div>
);
}
function Right1(props) {
return (
<div>
<h1>Right1</h1>
<Right2 number={props.number} setNumber={props.setNumber}> </Right2>
</div>
);
}
function Right2(props) {
return (
<div>
<h1>Right2</h1>
<Right3 number={props.number} setNumber={props.setNumber}></Right3>
</div>
);
}
function Right3(props) {
return (
<div>
<h1>Right3</h1>
<input
type="button"
value="+"
onClick={() => {
props.setNumber((prev) => prev + 1);
}}
></input>
</div>
);
}
위와 같이 Root(부모, 최상위) 컴포넌트에서 useState를 사용하여 state 생성 후 자식 컴포넌트에게 state를 전달하기 위해서는 props drilling이 필요하다. 매우 비효율 적이고 중첩된 컴포넌트의 개수가 많아 진다면 코딩의 실수 도 발 생할 수 있다.
또한, 현재 Left3에서만 state가 변경 되고 있지만, console을 찍어보면 Left1 , Left2에서도 리렌더링이 일어나고 있음.
<장점>
1. 코드의 간결성 증가, 복잡성 감소
2. 전역 state를 가져온 컴포넌트에서만 리렌더링 발생
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
현재 createStore는 RTK의 configureStore를 사용하라고 권장되고 있지만, 학습을 위해 사용
- Provider : store에 있는 state들을 어떤 컴포넌트에 제공할 것인지 정해주는 wrapper 컴포넌트
- useSelector : 어떤 state를 선택해서 해당 컴포넌트에서 사용할 것인지 정해주는 함수
- useDispatch : 해당 state 값을 변경 시킬 때 사용
// 리듀서 : store 안에 있는 state를 어떻게 바꿀 것인가
function reducer(state = { number : 1 }, action) {
switch (action.type) {
case 'PLUS':
return {
...state,
number: state.number + 1,
}
default:
return state;
}
}
// 스토어 생성
const store = createStore(reducer);
Left1 code ...
Left2 code ...
function Left3() {
// useSelector를 이용해서 number state를 사용하겠다고 명시
const number = useSelector((state) => state.number);
return (
<div>
<h1> Left3 : {number} </h1>
</div>
);
}
Right1 code ...
Right2 code ...
function Right3() {
// useDispatch를 사용하여 특정 state의 값을 변경하겠다.
const dispatch = useDispatch();
return (
<div>
<h1>Right3</h1>
<input
type="button"
value="+"
onClick={() => {
// dispatch 에는 action object를 넘겨주는 것!
dispatch({ type: 'PLUS' }); // dispatch 가 reducer를 호출!!
}}
></input>
</div>
);
}
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
function reducer(state, action) {
switch (action.type) {
case 'up':
return {
...state,
value: state.value + action.step, // action.step 적용
};
default:
return state;
}
}
const initialState = {value: 0}; // initialState 지정
// initialState를 store 생성 시 선언
const store = createStore(reducer, initialState);
function Counter() {
// value state를 사용하겠다.
const count = useSelector((state) => state.value);
const dispatch = useDispatch();
return (
<div>
<button
onClick={() => {
// dispatch 에는 action object를 넘겨주는 것!
dispatch({ type: 'up', step: 2 });
}}
>
+
</button>
{count}
</div>
);
}
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
// slice 생성
const counterSlice = createSlice({
name: 'counter',
initialState: {value: 0},
reducers: {
up: (state, action) => {
state.value = state.value + action.payload;
}
}
})
// 다른 파일에서 사용할 슬라이스나, action 함수 export 시키기
export default counterSlice;
export const { up } = counterSlice.actions;
// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './counterSlice';
// store 생성
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
})
// store export
export default store;
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';
import { up } from './counterSlice';
function Counter() {
// useSelector 사용하여 counter 의 value state를 사용하겠다.
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
// dispatch 안에 action 함수인 up을 사용
// up(2) 실행 시 action.payload로 2가 전달 됨
<button onClick={() => dispatch(up(2))}>+</button>
{count}
</div>
);
}
export default function App() {
return (
// Provider로 감싸줘야함!
<Provider store={store}>
<div>
<Counter></Counter>
</div>
</Provider>
);
}
createAsyncThunk는 비동기 작업을 처리하는 action 을 만들어 준다.
동기적인 작업은 reducers를 사용, 비동기적인 작업은 extraReducers를 사용