0

Here I implemented redux like store using custom hooks. everything goes well and code executed correctly but problem is that in reducer under switch statement "TOGGLE" there I return a updated state which is finally stored in globalstate but if I returned empty object {} instead of {products: updated} still globalstate updating correctly with a change that has been done in reducer...since i am not passing globalstate reference then how it is updated correctly

and what listeners exactly do in dispatch method in code

import MarkFavMulti from "./MarkFavMulti";
import classes from "./MarkFav.module.css";
import useStore from "../HookStore/Store";
import {reducer2} from "../SampleReducer";

const MarkFav = props => {
    const [outfit, dispatch] = useStore(reducer2);

    const onClicked = (id) => {
        dispatch({type: "TOGGLE", id: id});
    }

    const element = outfit.products.map((item) => {
        return <MarkFavMulti cloth={item.name}
            favorite={item.favorite}
            price={item.price}
            key={item.id}
            clicked={onClicked.bind(this, item.id)} />
    });

    return (
        <main className={classes.Markfav}>
            {element}
        </main>
    );
};




export default MarkFav;
import {useState, useEffect} from "react";

let globalState = {};
let listeners = [];

const useStore = (reducer) => {
    const setState = useState(globalState)[1];

    const dispatch = (action) => {
        let curr = Object.assign({},globalState);

        const newState = reducer({...curr}, action)
        globalState = {...globalState,...newState};

        for(let listener of listeners) {
            listener(globalState);
        }
    };

    useEffect(()=>{
        listeners.push(setState);
        return () => {
            listeners.filter(item => item !==setState);
        }
    },[setState]);

    return [globalState, dispatch];
};

export const initStore = (initialState) => {
    if(initialState) {
        globalState = {...globalState, ...initialState};
    }
}

export default useStore;

let initialState = {
    products: [
        { id: 1, name: "shirt", price: "$12", favorite: false },
        { id: 2, name: "jeans", price: "$42", favorite: false },
        { id: 3, name: "coat", price: "$55", favorite: false },
        { id: 4, name: "shoes", price: "$8", favorite: false },

    ]
}

const configureStore = () => {
    initStore(initialState);
};

export default configureStore;

export const reducer2 = (state=initialState, action) => {
    switch (action.type) {
        case "TOGGLE":
            let update = {...state};
            let updated = [...update.products];

            updated = updated.map(item => {
                if(item.id === action.id) {
                    item.favorite = !item.favorite;
                    return item;
                }

                return item;
            }); 
            return {products: updated}; 
//if we return {} ...it will updated correctly in globalstate

        default:
            throw new Error("not reachable");
    }
} 
laxman
  • 1
  • 1

1 Answers1

0

The behavior that you are describing is due to this object assignment right here:

item.favorite = !item.favorite;

Here you are directly mutating the properties of the item object. You probably thought that it would be fine since you are using a copy of the products array.

let update = {...state};
let updated = [...update.products];

What actually happens is that updated is a "shallow copy" of the original array. The array itself is a new array, but the items in that array are the same items as in the state. You can read more about that here.

You need to return a new item object instead of mutating it. Here's a concise way to write it using the ternary operator.

   case "TOGGLE":
      return {
        ...state, // not actually necessary since products is the only property
        products: state.products.map((item) =>
          item.id === action.id
            ? {
                ...item,
                favorite: !item.favorite
              }
            : item
        )
      };
Linda Paiste
  • 38,446
  • 6
  • 64
  • 102