반응형

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

발생 Error

React에서 다음 Source Code를 실행할 경우,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createSlice } from '@reduxjs/toolkit';
import React from 'react';
 
const initialState = {count: 0};
 
const countSlice = createSlice({
    name'count',
    initialState,
    reducers: {
        increment(state) {state.count + 1},
    },
 
});
 
export default countSlice;
 
 
 

🚨 다음과 같은 오류 발생

Expected an assignment or function call and instead saw an expression

 

 

발생 원인

state.count 값을 변화시키기 위한 표현식이 잘못 기재되어있음

state.count + 1은 state.count 값을 변화시키기는 것이 아니라 반환된 값에 1을 더하는 표현식

 

 

해결 방법

state.count + 1을 변화시키는 수식으로 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createSlice } from '@reduxjs/toolkit';
import React from 'react';
 
const initialState = {count: 0};
 
const countSlice = createSlice({
    name'count',
    initialState,
    reducers: {
        increment(state) {state.count += 1},
    },
 
});
 
export default countSlice;
 
 
 

 

반응형
반응형

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

발생 Error

React에서 다음 Source Code를 실행할 경우,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
 
reportWebVitals();
 
 
 

🚨 다음과 같은 오류 발생

could not find react-redux context value;

please ensure the component is wrapped in a <Provider>

 

 

발생 원인

Redux store를 전역으로 사용하기 위한 설정, store prop 공유가 기재되어있지 않음

 

 

해결 방법

Redux 스토어를 전역적으로 제공하기 위해 Provider 선언 및 store prop 전달

 - Provider: 하위 컴포넌트인 App 컴포넌트와 그 아래에 있는 모든 컴포넌트에 Redux 스토어를 제공
 - store: Redux 스토어 객체, Provider 컴포넌트의 store prop으로 전달되어 모든 컴포넌트에 공유됨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
 
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './store/index';
 
import './index.css';
 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);
 
reportWebVitals();
 
 
 

 

반응형
반응형

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

발생 Error

React에서 다음 Source Code를 실행할 경우,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { cat, chick, dog, tiger } from '../../store/modules/animal';
 
const Animal = () => {
    const name = useSelector(state => state.animal.name);
    // animal reducer 안의 name
    const crying = useSelector(state => state.animal.crying);
    const dispatch = useDispatch(); // action을 발생시키는 함수
 
    return (
        <div>
            <h1>동물의 울음소리</h1>
            <h1>{name} : {crying}</h1>
            <p>
                <button onClick={() => dispatch(tiger)}>호랑이</button>
                {/*
                dispatch(tiger): →
                index.js → 
                export const tiger = () => ({type: TIGER});
                */}
                <button onClick={() => dispatch(dog)}>강아지</button>
                <button onClick={() => dispatch(cat)}>고양이</button>
                <button onClick={() => dispatch(chick)}>병아리</button>
            </p>
        </div>
    );
};
 
export default Animal;
 
 
 

🚨 다음과 같은 오류 발생

Actions must be plain objects. Use custom middleware for async actions.

 

 

발생 원인

dispatch의 매개변수로 함수 포인터를 선언

 

 

해결 방법

dispatch의 매개변수를 객체로 선언

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { cat, chick, dog, tiger } from '../../store/modules/animal';
 
const Animal = () => {
    const name = useSelector(state => state.animal.name);
    // animal reducer 안의 name
    const crying = useSelector(state => state.animal.crying);
    const dispatch = useDispatch(); // action을 발생시키는 함수
 
    return (
        <div>
            <h1>동물의 울음소리</h1>
            <h1>{name} : {crying}</h1>
            <p>
                <button onClick={() => dispatch(tiger())}>호랑이</button>
                {/*
                dispatch(tiger): →
                index.js → 
                export const tiger = () => ({type: TIGER});
                */}
                <button onClick={() => dispatch(dog())}>강아지</button>
                <button onClick={() => dispatch(cat())}>고양이</button>
                <button onClick={() => dispatch(chick())}>병아리</button>
            </p>
        </div>
    );
};
 
export default Animal;
 
 
 

 

반응형
반응형

이 글은 [[한글자막] React 완벽 가이드 with Redux, Next.js, TypeScript]를 수강하며 정리한 글입니다.

 

 

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

Redux 사용법

1. createSlice 선언

: initialState, reducer 함수의 객체, slice의 이름을 받아서 reducer와 state에 해당하는 action 생성자와 action type을 자동으로 생성하는 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createSlice } from "@reduxjs/toolkit";
 
const uiSlice = createSlice({
    name'ui',
    initialState: {cartIsVisible: false},
    reducers: {
        toggle(state){
            state.cartIsVisible = !state.cartIsVisible;
        }
    },
});
 
export const uiActions = uiSlice.actions;
 
export default uiSlice;
 
 
 

 

2. consigureStore에 reducer 전달

: reducer 매개변수를 통해 루트 리듀서를 지정할 수 있으며, 여러 개의 리듀서를 조합하여 사용할 수 있음

* reducer: 이전 상태와 액션을 받아와서 새로운 상태를 반환, Redux 애플리케이션에서 상태의 변화는 액션을 디스패치하면 리듀서를 통해 이루어짐

1
2
3
4
5
6
7
8
9
import { configureStore } from '@reduxjs/toolkit';
import uiSlice from './ui-slice';
 
const store = configureStore({
    reducer: {ui: uiSlice.reducer}
});
 
export default store;
 
 
 

 

3. 전체 Application에 redux store 공유

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
 
import './index.css';
import App from './App';
import store from './store';
 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store={store}><App /></Provider>);
 
 
 

 - Provider: 하위 컴포넌트인 App 컴포넌트와 그 아래에 있는 모든 컴포넌트에 Redux 스토어를 제공

 - store: Redux 스토어 객체, Provider 컴포넌트의 store prop으로 전달되어 모든 컴포넌트에 공유됨

 

4. useDispatch를 통한 action 전달

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import classes from './CartButton.module.css';
 
import {uiActions} from '../../store/ui-slice'
import { useDispatch } from 'react-redux';
 
const CartButton = (props) => {
  const dispatch = useDispatch();
  
  const toggleCartHandler = () => {
    dispatch(uiActions.toggle());
  };
  
  return (
    <button className={classes.button} onClick={toggleCartHandler}>
      <span>My Cart</span>
      <span className={classes.badge}>1</span>
    </button>
  );
};
 
export default CartButton;
 
 
 

 - click 이벤트에 toggle 핸들러를 추가

 - useDispatch: 생성한 action을 작동시킴

 - uiActions.toggle(): Redux 액션을 반환하는 액션 생성자 함수

[정리]

버튼 클릭

 → dispatch(uiActions.toggle()) 호출

 → Redux 스토어에 uiActions.toggle()이 반환한 액션 객체를 디스패치

 → Redux 스토어는 전달받은 액션 객체의 type 속성*을 기반으로 어떤 리듀서를 호출할지 결정하여 리듀서에게 액션 객체와 현재의 상태를 전달

* createSlice.actions을 사용하면 type 속성을 unique identifier로 자동 생성

 → 리듀서는 전달받은 액션의 타입을 확인하여 어떤 작업을 수행할지 결정

 

5. useSelector를 통한 Redux 상태 객체에서 특정 속성을 선택하여 반환하여 조건부 화면 반환 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { useSelector } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
 
function App() {
  const showCart = useSelector((state) => state.ui.cartIsVisible);
 
  return (
    <Layout>
      {showCart && <Cart />}
      <Products />
    </Layout>
  );
}
 
export default App;
 
 
 

* 여기서는 Redux 저장소에서 ui의 cartIsVisible 값을 선택하여 반환

ui = redux 저장소의 uiSlice.reducer(=새로운 상태를 반환하는 함수)

ui.cartIsVisible = uiSlice.reducer를 통해 cartIsVisible의 기존 state를 받아 신규 state를 반환

 

결과

Redux를 활용한 toggleBtn 동작 구현

 

 

 

참고 자료

 

createSlice | Redux Toolkit

 

redux-toolkit.js.org

 

반응형
반응형

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

발생 Error

React에서 다음 Source Code를 실행할 경우,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import classes from './CartButton.module.css';
 
import uiActions from '../../store/ui-slice'
import { useDispatch } from 'react-redux';
 
const CartButton = (props) => {
  const dispatch = useDispatch();
  
  const toggleCartHandler = () => {
    dispatch(uiActions.toggle());
  };
  
  return (
    <button className={classes.button} onClick={toggleCartHandler}>
      <span>My Cart</span>
      <span className={classes.badge}>1</span>
    </button>
  );
};
 
export default CartButton;
 
 
 

🚨 toggleCartHandler가 onClick 메서드로 걸린 버튼 클릭 시, 다음과 같은 오류 발생

TypeError: _store_ui_slice__WEBPACK_IMPORTED_MODULE_1__.default.toggle is not a function

 

 

발생 원인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createSlice } from "@reduxjs/toolkit";
 
const uiSlice = createSlice({
    name'ui',
    initialState: {cartIsVisible: false},
    reducers: {
        toggle(state){
            state.cartIsVisible = !state.cartIsVisible;
        }
    },
});
 
export const uiActions = uiSlice.actions;
 
export default uiSlice;
 
 
 

uiSlice를 default로 export, uiActions는 개별 함수로 export

default로 export하지 않은 uiActions를 {}없이 import

 

 

해결 방법

⭐ export default로 내보낸 것만 중괄호 없이 import
이 외export 문은 중괄호를 사용하여 import

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import classes from './CartButton.module.css';
 
import {uiActions} from '../../store/ui-slice'
import { useDispatch } from 'react-redux';
 
const CartButton = (props) => {
  const dispatch = useDispatch();
  
  const toggleCartHandler = () => {
    dispatch(uiActions.toggle());
  };
  
  return (
    <button className={classes.button} onClick={toggleCartHandler}>
      <span>My Cart</span>
      <span className={classes.badge}>1</span>
    </button>
  );
};
 
export default CartButton;
 
 
 

 

반응형