myTest532 myTest532
myTest532 myTest532

Reputation: 2381

React Redux using Hooks

I'm trying to create my first react redux using Hooks.

I have a state folder inside my src folder. Inside my state folder I have another folder called reducer and a accountReducer.js file accountReducer.js:

const reducer = (state = 0, action) => {
    switch(action.type) {
        case "deposit":
            return state + action.payload;
        case "withdraw":
            return state - action.payload;
        default:
            return state;
    }
}

export default reducer;

Also, I have a index.js to combine and export my reducers

import { combineReducers } from "redux";
import AccountReducer from './accountReducer';

const reducers = combineReducers({
    account: AccountReducer
});

export default reducers;

In the state folder, I have my store.js

import { createStore } from "redux";
import reducers from './reducers/index';

export const store = createStore(
    reducers,
    {}
);

I also have an action-creator folder with an index.js file for my actions

export const depositMoney = (amount) => {
    return (dispatch) => {
        dispatch({
            type: "deposit",
            payload: amount
        });
    }
}

export const withdrawMoney = (amount) => {
    return (dispatch) => {
        dispatch({
            type: "withdraw",
            payload: amount
        });
    }
}

Also, in the state folder I have a index.js to export my actionCreators

export * as actionCreators from "./action-creators/index";

In the main index.js file I create a Provider for my store

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from "react-redux";
import { store } from './state/store';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Finally, in the App.js I test my redux

import { useSelector, useDispatch } from "react-redux";
import { bindActionCreators } from "redux";
import { actionCreators } from './state/index';

function App() {

  const account = useSelector((state) => state.account);
  const dispatch = useDispatch();

  const { depositMoney, withdrawMoney } = bindActionCreators(actionCreators, dispatch);

  return (
    <div className="App">
      <h1>{account}</h1>
      <button onClick={() => depositMoney(500)}>Deposit</button>
      <button onClick={() => withdrawMoney(250)}>Withdraw</button>
    </div>
  );
}

export default App;

When I click on Deposit or Withdraw it returns:

Error: Actions must be plain objects. Instead, the actual type was: 'function'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.

What am I missing in my code?

Please any advice in my folder structure would be appreciate as well. enter image description here

Upvotes: 0

Views: 322

Answers (3)

franklin
franklin

Reputation: 89

So i tried out fixing the issue you had using redux toolkit. Seems quite straight forward using redux toolkit.

in reducers.js where i define the different possible actions (deposit and withdrawal) and an initial balance.

import { createSlice } from "@reduxjs/toolkit"
//exporting the accountSlice to be passed to the store as a reducer. 
export const accountSlice = createSlice({
    name: 'account',
    initialState: {
        account: 0
    },
    reducers: {
        depositMoney: (state, action) => {
            state.account = state.account + action.payload
        },
        withdrawMoney: (state, action) => {
            state.account = state.account - action.payload
        },
    }
})
//exporting the deposit and withdraw functions and balance (initial balance and the result from deposit or withdrawal)
export const {depositMoney, withdrawMoney} = accountSlice.actions;

export const balance = (state) => state.account.account

next i configure my store.

import { configureStore } from "@reduxjs/toolkit"
import { accountSlice } from "./Reducers"
//exporting store to be passed down to the app
export const store = configureStore({
    reducer: {
        account: accountSlice.reducer
    },    
})

implement the the deposit and withdrawal functions in my AccountDisplay component.

import { useSelector, useDispatch } from "react-redux";
import { balance, depositMoney, withdrawMoney } from './Reducers';

const AccountDisplay = () => {
    const dispatch = useDispatch()
    const accountBalance = useSelector(balance)

    return (
        <div className="App">
            <h1>{accountBalance}</h1>
            <button onClick={() => dispatch(depositMoney(500))}>Deposit</button>
            <button onClick={() => dispatch(withdrawMoney(250))}>Withdraw</button>
        </div>
    );
}

export default AccountDisplay;

finally, bringing it all together in my App.js

import AccountDisplay from "./AccountDisplay";
import { Provider } from "react-redux";
import { store } from "./store";

const App = () => {

  return (
    <Provider store={store}>
      <div>
        <AccountDisplay />
      </div>
    </Provider>
  );
}

export default App;

Upvotes: 0

Ron B.
Ron B.

Reputation: 1540

Just like the error message suggets, you need to add redux-thunk in order to be able to use dispatch with functions. Read about it here.

According to the first example, change the code in store.js to:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers/index';


export const store = createStore(reducers, applyMiddleware(thunk));

Upvotes: 2

Asaf Aviv
Asaf Aviv

Reputation: 11760

You don't need to return a function if you don't do anything asynchronous, so you can just change your action creators to return the action object.

export const depositMoney = amount => ({
  type: 'deposit',
  payload: amount,
})

export const withdrawMoney = amount => ({
  type: 'withdraw',
  payload: amount,
})

If you do need to do something asynchronous inside your action creators you can use the redux-thunk middleware

Upvotes: 3

Related Questions