Search code examples
javascriptreactjsreact-hooksreduceredux-toolkit

When I combine two reducers in the store, my maps with lists stop working in the project and show an error


My store and reducers When I combine two reducers in the store, my maps with lists stop working in the project and show an error. Error - Uncaught TypeError: tovars.map is not a function

import { configureStore } from "@reduxjs/toolkit"; 
import ordersReducer from "../components/content/tovars/tovarsSlice";
import loginReducer from "../components/content/login/loginSlice";

const stringMiddleware = () => (next) => (action) => {
    if (typeof action === 'string') {
        return next({
            type: action
        })
    }
    return next(action)
};

const store = configureStore({
        reducer: {
            tovars: ordersReducer,
            login:  loginReducer
        },
        middleware: getDefaultMiddleware => getDefaultMiddleware().concat(stringMiddleware),
        devTools: process.env.NODE_ENV !== 'production', 
    })
export default store;

I tried to do const tovars = useSelector((state) => state.tovarsList);, but it doesn't work

But when I leave only one reducer in the store, everything works.

My component After connecting the second reducer, an error occurs on all map functions in the project.

const Tovars = () => {
    const dispatch = useDispatch();
    const tovars = useSelector(tovarsList);
    const showBasket = useSelector((state) => state.showBasket);
    const displayedTovars = useSelector((state) => state.displayedTovars)

    const { loadingTovar, errorLoading } = useLoadingStatus();
    const listRef = useRef(null);

useEffect(() => {
    dispatch(fetchTovars());
},[dispatch])

useEffect(() => {
      listRef.current?.scrollIntoView()
  }, [tovars,displayedTovars]);

const renderTovars = useMemo(() =>{

      const list = tovars.map((item,id) => (
            <li key={item.id}>
                <div className="content_card">
                    <p className="title_card">{item.title}</p>
                    <p className="category_card">{item.category}</p>
                    <NavLink to="/InfoCard"  className="nav-link">
                        <img src={item.image} 
                             alt="card"
                             onClick={() => dispatch(selectCard(item,item.id))} />
                    </NavLink>
                    <div className="price_rating_card">
                         <p className="price_card">Price: {item.price} $</p>
                    </div>
                    <div className="del_card">X</div>
                    <button className="card_buy"
                            onClick={() => dispatch(addTovar(item))
                            }                     
                    >Buy</button>
                </div>
                
            </li>
        )).slice(0, displayedTovars)

    return <ul className="list">{list}
               <div ref={listRef}></div>
           </ul>
},[dispatch, tovars, displayedTovars]) 

const nextClickTovars = useCallback(() => {
      dispatch(nextDisplayedTovars(displayedTovars + 6))
},[dispatch, displayedTovars])

const backClickTovars = useCallback(() => {
      if(displayedTovars <= 6){
        return false;
      }
      dispatch(backDisplayedTovars(displayedTovars - 6)) 
},[dispatch, displayedTovars])

    return(<div className="content">
            <div className="filter">
                <Filter />
                <ShowDiscounts />
            </div>
            <div className="spinner_tovars">
                  {loadingTovar}
                  {errorLoading}
            </div>
             <div className="content_tovars_pagination" >
                <Pagination nextTovars={nextClickTovars}
                            backTovars={backClickTovars}
                 />
                  {renderTovars}
                <Pagination nextTovars={nextClickTovars}
                            backTovars={backClickTovars}
                 />
             </div> 
                  {showBasket && <BasketModal />}
           </div>)
}
export default Tovars;

Solution

  • When the reducer functions are passed to the reducer property of the store configuration, the state is tovars, e.g. state.tovars.

    reducer: {
      tovars: ordersReducer,
      login: loginReducer
    },
    

    In the component ensure you are selecting the correct state.

    Example:

    const tovars = useSelector(state => state.tovars);
    

    Or ensure that the tovarsList selector function is correctly accessing and selecting the correct state.

    Example:

    const tovarsList = state => state.tovars;
    
    const tovars = useSelector(tovarsList);
    

    Ensure that all dispatched action that update the state.tovars state maintains the correct state invariant. Based on the usage it appears that state.tovars is an array. Make sure the local tovars variable of the selected state is an array value such that tovars.map is a function that is callable when rendering. If this isn't possible to maintain the state invariant then at least refactor the selector function to guarantee a returned array if state.tovars is falsey.

    Example:

    const tovarsList = state => state.tovars || [];
    
    const tovars = useSelector(tovarsList);