Home > Software engineering >  React Redux Array state change in Function Component not working
React Redux Array state change in Function Component not working

Time:01-08

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> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
       <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> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
       <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;
  • Related