useEffect is not working even through the array state changed.
I am first adding the amount in to transactions array through dispatch(addTransaction(10))
.
Then I want to count the array sum by dispatching dispatch(countTotal(transactions))
.
I am not sure where its going wrong.
import logo from './logo.svg';
import './App.css';
import { useState, useEffect } from 'react'
import { addTransaction } from './actions/transActions';
import { countTotal } from './actions/countActions';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
function App() {
const transactions = useSelector(state => {
return state.trans.transactions
});
const count = useSelector(state => state.count.total);
const dispatch = useDispatch()
useEffect(() => {
console.log("useEffect", transactions)
dispatch(countTotal(transactions))
}, [transactions])
console.log('transactions', transactions)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>{count}</h1>
<br></br>
<div>
<button style={{width:90, height:60, fontSize: 40, backgroundColor: 'teal', border: '0px', color: 'white'}} onClick={() => {
dispatch(addTransaction(10))
}} type="button"> 10</button>
<button style={{width:90, height:60, fontSize: 40, backgroundColor: 'indianred', border: '0px', color: 'white'}} onClick={() => dispatch(addTransaction(-10))} type="button"> - 10 </button>
</div>
</header>
</div>
);
}
export default App;
addTransaction code:
export const addTransaction = (amount) => (dispatch) => {
dispatch({
type: "ADD_TRANSACTION",
payload: amount,
})
}
Reducer code :
const initialState = {
transactions : []
};
export default function(state = initialState, action) {
switch (action.type) {
case "ADD_TRANSACTION":
console.log('ADD_TRANS', action.payload)
let trans = state.transactions;
trans.push(action.payload)
return {
...state,
transactions: trans
};
default:
return state;
}
}
Everything is working fine. Even dispatch(countTotal(transactions))
also working. Except when I am triggering it in useEffect its not working.
CodePudding user response:
Issue got fixed by changing reducer part o the code as array getting updated but state change is detecting.
const initialState = {
transactions : []
};
export default function(state = initialState, action) {
switch (action.type) {
case "ADD_TRANSACTION":
console.log('ADD_TRANS', action.payload)
let trans = [...state.transactions, action.payload]
return {
...state,
transactions: trans
};
default:
return state;
}
}
This fixed the issue.
CodePudding user response:
You cannot use push
in a reducer in "bare Redux", because it is mutating the state, but the state should be considered immutable. Also you should not use bare Redux, instead you should use Redux Toolkit, which uses a library under the hood that makes it possible to use push
and any other mutating code without any risk for similar bugs.
CodePudding user response:
Something is off in your addTransaction
action builder. That should return an object with action type and payload, instead you are using a currying function. The dispatch
instance accessed via useDispatch
hook is the one you need to correctly forward the action object to the store.
export const addTransaction = (amount) => ({
type: "ADD_TRANSACTION",
payload: amount,
})
CodePudding user response:
Because you provided transactions
in the dependency array of useEffect
. When you provide some state in useEffect
dependency array, it means the useEffect
will only trigger when that state changes. If you need to call countTotal
everytime the component renders and transactions
changes, you can use two useEffect
.
Read this: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
CodePudding user response:
If you don't want to change your code too much and want to continue with this code then there is a quick fix for this issue. You should take and extra state in the reducer and toggle it and start listening to it Like below
Reducer Code:
const initialState = {
transactions : [],
refresh: false
};
export default function(state = initialState, action) {
switch (action.type) {
case "ADD_TRANSACTION":
console.log('ADD_TRANS', action.payload)
let trans = state.transactions;
trans.push(action.payload)
return {
...state,
transactions: trans
refresh: !state.refresh
};
default:
return state;
}
}
Main file:
import logo from './logo.svg';
import './App.css';
import { useState, useEffect } from 'react'
import { addTransaction } from './actions/transActions';
import { countTotal } from './actions/countActions';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
function App() {
const transactions = useSelector(state => {
return state.trans.transactions
});
const refresh = useSelector(state => state.trans.refresh);
const count = useSelector(state => state.count.total);
const dispatch = useDispatch()
useEffect(() => {
console.log("useEffect", transactions)
dispatch(countTotal(transactions))
}, [transactions, refresh])
console.log('transactions', transactions)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>{count}</h1>
<br></br>
<div>
<button style={{width:90, height:60, fontSize: 40, backgroundColor: 'teal', border: '0px', color: 'white'}} onClick={() => {
dispatch(addTransaction(10))
}} type="button"> 10</button>
<button style={{width:90, height:60, fontSize: 40, backgroundColor: 'indianred', border: '0px', color: 'white'}} onClick={() => dispatch(addTransaction(-10))} type="button"> - 10 </button>
</div>
</header>
</div>
);
}
export default App;