React useReducer action object is undefined, dispatch passed as context

46 views Asked by At

I am using react router v6 I am trying to change the state variable (stored in context) via dispatch function (dispatch function also passes as context)

Here is the main.tsx with context provider

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    loader: filmsLoader,
    children: [
      {
        path: "/",
        element: <FilmsGrid />
      }
    ]
  }
]);

ReactDOM.createRoot(document.getElementById('root')!).render(
  <CssBaseline>
    <Provider>
      <RouterProvider router={router} />
    </Provider>
  </CssBaseline>
);

This is the file with context and reducer

import React, { createContext, useContext, useReducer } from 
"react";


const FilterContext = createContext('popular');
const FilterReducerContext = createContext(null);

function filterReducer({state, action}) {
    console.log(action.type); // Here is undefined 
    switch (action) {
        case "popular": {
            return {
                filterState: "popular"
            };
        }
        case "release-date": {
            return {
                filterState: "release-date"
            };
        }
        default: return state;
    }
}

export default function Provider({ children }) {
    const [filterState, dispatch] = useReducer(filterReducer, 
'popular');

    return (
        <FilterContext.Provider value={filterState}>
            <FilterReducerContext.Provider value={dispatch}>
                { children }
            </FilterReducerContext.Provider>
        </FilterContext.Provider>
    );
}


export function useFilterContext() {
    return useContext(FilterContext);
}
export function useFilterReducer() {
    return useContext(FilterReducerContext);
}

Component that uses dispatch

import { Box, FormControl, InputLabel, MenuItem, Select } from "@mui/material";
import { useFilterContext, useFilterReducer } from "../../../FilterContext";

function SortType() {
    const filterState = useFilterContext();
    const dispatch = useFilterReducer();

    const changeSortType = (event) => {
        dispatch({
            type: event.target.value
        })
    };

    return (
        <Box 
        width={'100%'}
        sx={{
            padding: 2
        }}
        >
            <FormControl
            fullWidth
            >
                <InputLabel
                    id={'sortType-select'}
                    sx={{
                        "&.MuiInputLabel-root": {
                            left: -12
                        }
                    }}
                >Sort by:</InputLabel>
                <Select
                labelId="sortType-select"
                variant="standard"
                onChange={event => changeSortType(event)}
                value={filterState}
                >
                    <MenuItem value={'popular'}>Popular</MenuItem>
                    <MenuItem value={'release-date'}>Release date</MenuItem>
                </Select>
            </FormControl>
        </Box>
    );
}

export default SortType;

I tried change a state variable using dispatch function:

  1. value changed -> dispatch is called
  2. reducer reads action value and changes a state variable
  3. a state variable then passes with context and can be used as a state

I got: reducer function doesn't get action object

I got error "cannot read properties of undefined" when reducer is trying to read an action object

1

There are 1 answers

0
Didi On

In this block of code, filterReducer gets arguments incorrectly, it should get it like reducer(state, action), not reducer({state, action}).

That is why action is undefined

import React, { createContext, useContext, useReducer } from 
"react";


const FilterContext = createContext('popular');
const FilterReducerContext = createContext(null);

function filterReducer({state, action}) {
    console.log(action.type); // Here is undefined 
    switch (action) {
        case "popular": {
            return {
                filterState: "popular"
            };
        }
        case "release-date": {
            return {
                filterState: "release-date"
            };
        }
        default: return state;
    }
}

export default function Provider({ children }) {
    const [filterState, dispatch] = useReducer(filterReducer, 
'popular');

    return (
        <FilterContext.Provider value={filterState}>
            <FilterReducerContext.Provider value={dispatch}>
                { children }
            </FilterReducerContext.Provider>
        </FilterContext.Provider>
    );
}


export function useFilterContext() {
    return useContext(FilterContext);
}
export function useFilterReducer() {
    return useContext(FilterReducerContext);
}