11

My Home.js component just doesn't seem to see the dispatch function at all. Do you guys know why? I'm kinda new to redux style state management stuff in redux.

I keep getting the error "TypeError: dispatch is not a function"

App.js

import React from 'react';
import { HashRouter, Route } from 'react-router-dom';

import Home from './pages/Home';
import Start from './pages/Start';
import Result from './pages/Result';

import RPSContextProvider from './contexts/RPSContext';

const App = () => {
    return (
        <HashRouter>
            <RPSContextProvider>
                <Route exact path="/" component={Home} />
                <Route path="/start" component={Start} />
                <Route path="/result" component={Result} />
            </RPSContextProvider>
        </HashRouter>
    );
};

export default App;

Home.js

import React, { useRef, useContext } from 'react';
import { RPSContext } from '../contexts/RPSContext';
import './home.css';

const Home = (props) => {
    const { state, dispatch } = useContext(RPSContext);
    const playerNameEntry = useRef();

    const handleClick = () => {
        if (!isStringEmpty(playerNameEntry.current.value)) {
            dispatch({ type: 'SET_NAME', state: playerNameEntry.current.value });
            props.history.push({
                pathname: '/start'
            });
            console.log(dispatch);
        }
    };

    const isStringEmpty = (string) => string.trim().length === 0;

    return (
        <div className="app-container">
            <h1>
                You dare battle me at
                <br />
                Rock, Paper, Scissors?
                <br />
                You got no chance, kid!
            </h1>
            <p>What's your name, ya chancer?</p>
            <input type="text" onKeyPress={(e) => handleKeyPress(e)} ref={playerNameEntry} />
            <button onClick={handleClick}>Start</button>
        </div>
    );
};

export default Home;

RPSContext.js

import React, { createContext, useReducer } from 'react';
import { RPSReducer } from '../reducers/RPSReducer';

export const RPSContext = createContext();

const RPSContextProvider = (props) => {
    const [ state, dispatch ] = useReducer(RPSReducer, { playerName: '' });

    return <RPSContext.Provider value={{ state, dispatch }}>{props.children}</RPSContext.Provider>;
};

export default RPSContextProvider;

RPSReducer.js

export const RPSReducer = (state, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return { playerName: action };
        default:
            throw new Error();
    }
};

Basically as a first step I just want to set the name of the entry. I know this is quite a lot of code just for what I'm doing, but just wanting to try out useReducer and useContext so that I can learn all this new stuff in React.

pyan
  • 865
  • 2
  • 12
  • 23

2 Answers2

3

I solved the problem by adding

    switch (action.type) {
        case 'SET_NAME':
            return { ...state, playerName: action.payload }

in my reducer, and in Home.js changed the state key I had in there to payload. Not 100% sure if it having the same name was effecting anything, but its much less confusing naming it payload.

    const handleClick = () => {
        if (!isStringEmpty(playerNameEntry.current.value)) {
            dispatch({ type: 'SET_NAME', payload: playerNameEntry.current.value });
pyan
  • 865
  • 2
  • 12
  • 23
3

Wrap the whole App with AppContext.Provider passing with state and dispatch, like below

<AppContext.Provider value={{ state, dispatch }}>
  <div className="App">
    <Compo />
  </div>
</AppContext.Provider>
Max
  • 49
  • 1
  • 8