Reputation: 2381
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.
Upvotes: 0
Views: 322
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
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
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