REDUX: Cannot read properties of undefined (reading 'type')

Hi again :sweat_smile:

I’m having issues figuring out what I’ve missed out here, for some reason my actions aren’t being picked up once they’re sent through the reducers upon store creation.

Where do I need to look at when this type of error comes up? my actions are set up like so:

actions/index.js

import { 
    GET_QUOTE, 
    SEND_TWEET 
} from "../constants";

export const getQuote = () => ({ 
    type: GET_QUOTE
})

export const sendTweet = () => ({
     type: SEND_TWEET
})

/reducers/getQuote.js

import { GET_QUOTE } from "../constants";

export default function updateQuote(state = null, action) {
    switch (action.type) {
        case GET_QUOTE:
            console.log("test");
        default:
            return state;
    }
}

/reducers/sentTweet.js

import { SEND_TWEET } from "../constants";

export default function sendTweet(state = null, action) {
    switch (action.type) {
        case SEND_TWEET:
            console.log("test");
        default:
            return state;
    }
}
import { combineReducers } from "redux";
import sendTweet from "./sendTweet";
import updateQuote from "./updateQuote";

export default combineReducers({
    sendTweet,
    updateQuote
})

/store/index.js

import {createStore} from "redux";
import reducer from "../reducers/index";

const initialState = {
    quotes: {
        "name1": "lorem ipsum",
        "name2": "lorem ipsum",
        "name3": "lorem ipsum",
        "name4": "lorem ipsum",
        "name5": "lorem ipsum",

    },
    name: [
        "name1", 
        "name2", 
        "name3", 
        "name4", 
        "name5"
    ],
    colors: [
        "red",
        "black",
        "cadetblue",
        "aliceblue",
        "yellow"
    ]

}


const store = createStore(initialState, reducer);

export default store;

any help and advice is much appreciated :slight_smile: thanks!

Don

Should I clarify further or is this not an appropriate question?

Can we see how you are dispatching the actions?

Hi Lasjorg,

Sure ^_^! Bear in mind, although I think it is obvious that this si very much an attempt at creating a skeleton that works and then filling it out later :sweat_smile:

import React from "react";
import store from "../store";
import { actionRegister } from "../actions";
import updateQuote from "../reducers/updateQuote";


// main export 
const QuoteBox = () => {
    // creating a variable pointing to the current store state
    const state = store.getState();

    // supporting functions
    const testFunc = () => {
        actionRegister.getQuote();
    }

    const testFunc2 = () => {
        actionRegister.sendTweet();
    }

    testFunc();
    testFunc2();



    // render 
    return (
        <form className="machine" id="quote-box" onSubmit>
                <h3 class="quote fade-in-image" id="text">
                    <i class="fas fa-quote-left"></i>
                    First, have a definite, clear practical ideal; 
                    a goal, an objective. Second, 
                    have the necessary means to achieve your ends; 
                    wisdom, money, materials, and methods. 
                    Third, adjust all your means to that end.
                </h3>
                <p class="fade-in-image" id="author">- Aristotle</p>
                <div class="buttons">
                    <a id="tweet-quote" href="#"><i class="fab fa-twitter-square"></i></a>
                    <a href="#"><i class="fab fa-tumblr-square"></i></a>
                    <button id="new-quote" value="New Quote">New Quote</button>
                </div>
            <p class="developer">by Don</p>
        </form>
    );
}

export default QuoteBox;


I changed the code for the dispatch a little to consolidate it and it now looks as such

import { bindActionCreators } from "redux";
import { 
    GET_QUOTE, 
    SEND_TWEET 
} from "../constants";

const getQuote = (payload) => ({ 
    type: GET_QUOTE
})

const sendTweet = (payload) => ({
     type: SEND_TWEET
})

export const actionRegister = bindActionCreators (
    {
        getQuote: getQuote,
        sendTweet: sendTweet
    },
    store.dispatch
)

You seem to be making this overcomplicated by using layers upon layers of abstraction here. You’re getting the state by calling store.getState() directly, why don’t you do

store.dispatch({ type: "THE_TYPE" })

Instead there are afaics at least three levels of indirection you’ve added on top of that – I can’t immediately see which one of them is causing you to lose the value, but in this case you’ve managed to make something that’s very simple extremely complicated.

(note as you’re doing this outside of react props/state, it’s just going to work once every time regardless, you’d need to refresh the screen to reset every time).

Also, are you following a very old tutorial here?

With the current react redux bindings you do essentially the same thing, like

const QuoteBox = () => {
  const state = useSelector((state) => state);
  const dispatch = useDispatch();

  return (
    <form className="machine" id="quote-box" onSubmit>
      ........

Edit edit: minor, but why is onSubmit set to true – you’ve written onSubmit={true} in that form definition? That is not a thing that you normally pass as an event callback.


Edit: following isn’t right, I skim read & didn’t register that you had the default case. Still think this will do something a bit weird but the function is just basically return state regardless. I’ll leave it there regardless in case there’s anything useful

This function is wrong (Edit: it’s not, but that’s because first case falls through which is what I missed). It needs to be

(State, Action) => State

ie a function that takes something (State) + an action (which is an object with the property type) and returns something that’s exactly the same shape as State.

Yours is (Edit: it’s not):

(State, Action) => undefined

So it won’t work, redux only works if the “reducer” always returns state (ie the reducer can always be replace with the value of whatever “state” is.

1 Like

Hey Dan,

Thank you for the very detailed feedback, I really appreciate it!

So with regards to your suggestions:

This isn’t clear in the code I posted, but I have tried simply writing the action into my dispatch, and the result still came out the same. when I run it through chrome developer tools, it seems to be pointing at the fact that the argument action isn’t being dispatched properly, and that whatever it is dispatching is lost for some reason.

You are correct, I was referencing some external documentation and had forgotten about the new methods available to do the exact same thing, thanks for the reminder!

this is an error, previously a function was assigned to this but in my fiddling around to find the problem I removed the assignment and didn’t delete the method :sweat:

This is the problem, the returning of the state doesn’t appear to be the problem itself, rather it’s the switch statement trying to access the action argument, when I run this without calling the action…

Just tried this, it then pointed towards my createStore method and… look what I did …

… its supposed to be const store = createStore(reducer, preloadedState) … three days I’ve spent trying to figure this out… sigh.

attention to detail.

Thanks Dan!

Aha

This is wrong, you have the arguments the wrong way round.

You also don’t need to use the second parameter, passing an initial state into the store is for when you do something like store the state of the app when someone closes it, then hydrate it when they next open it so it’s at the same place.

the state cannot be null here, this will be an error. The default value is supposed to be the default object you’re using for that bit of state, it never going to be null otherwise somethings seriously wrong in terms of the app

I’m also getting warnings/errors because action could be undefined, so in that case it couldn’t have a type property (which is mandatory for an action)

So like

NOTE re naming that your reducers are that piece of state. A reducer doesn’t represent an action. When you have some state in an app, it’s like:

{ quotes, names, colours }

The reducer being a function is just so that it can create a new version of the state, it’s just that piece of state, it’s completely interchangeable (at any point you can just replace it with the state object, that’s why you get really good debugging tools for redux that let you go backwards and forwards in time).

the following doesn’t make sense, logically, from a human language perspective:

{ getQuotes, sendTweet }
1 Like

Hi Dan,

Thank you! Appreciate this, I need to do a little more code reviewing and planning before proceeding.

1 Like

Ignore redux at first: think about the state, and what it needs to look like to make it easy for you handle it in the app.

You don’t need to split this into different reducers, just use one at first. If you do, then each reducer represents one key in your state:

{ quotes, names, colours }

Each one acts on one thing

function quotes (state = [], action) {}
function names (state = [], action) {}
function colours (state = [], action) {}

But then what if you need to act on a few at once? Is this going to make it harder or easier? Do you need something that cuts across two properties at once? Do you want them split like that? Will that make it easy in-app, or is it easier to just have

{ quotes }

Where quote is like { quote, name}? I don’t know, but you need to try out different things – as I say, figure out what works best in terms of the functionality based on you having an object with various properties you can access to render values in React components

1 Like

Just a quick note regarding something I can clearly remember being something that didn’t click immediately when I first looked at Redux (apologies if I’m writing something you already realise):

There is only ever one reducer function. If you write more than one to seperate out the logic, you then use combineReducers, and you’re joining them all together. Anything that results from the same type of action is going to be joined together.

Each reducer is a “slice” of the overall store object: quotes is a property keyed with quotes and a value that’s an array of quotes. names is a property keyed with names and a values that’s an array of names. It uses a function so that it can be updated, that’s all.

As a result of this, the actions are not tied to some specific reducer function logic in any way.

What I’m getting to is this:

Say you’ve got your original structure

{ quotes, names, colors }

And you have a reducer function for each one.

And you want to do something that affects quotes and names. You don’t need to figure out any complicated cross-cutting logic. You just add a branch for the action to both quotes and names

{ action: "ADD_QUOTE", quote: "some quote", name: "someone" }
function quotes (state = [], action) {
  switch (action.type) {
    case "ADD_QUOTE":
      // return updated quotes array
....

function names (state = [], action) {
  switch (action.type) {
    case "ADD_QUOTE":
      // return updated names array
....

Few additional notes:

So Facebook, on a user’s page there’s a section with all the notifications. There’s also, in the top bar, a little number next to the user icon that shows how many notifications there are. How do you keep the two in sync (bearing in mind there are a load of other UI stuff that need to be synchronised)?

Well, originally, didn’t. You needed to refresh the page.

One fix is put all the bits of state that had to be synced in a global object.

Two problems:

  1. Globals tend to be very difficult to work with, so there needed to be a very well-defined way to control getting and setting properties of the global object.
  2. React works by reacting to changes to props or state. If something was updated in that global object, it’s still the same object, so React doesn’t update.

Fix for 2. is that you just create a brand new global object every time a change is made.

Fix for 1. is you control access using something similar to pubsub.

  • you have a central store of data this is created using the createStore finction, which returns an object with three methods…
  • to get the most up-to-date data, can query the store directly (store.getState)…
  • …but anything can subscribe to the store (store.addListener).
  • when there is a change to the store, all subscribers are notified. This is what react-redux does, each component hooked up to redux is added as a subscriber.
  • finally, the store can only be updated by sending it a message (store.dispatch)
  • the message sent by dispatch has to be a plain object with a type property (doesn’t need anything else, but it must have that).
  • for comparison, that’s very much equivalent to things like browser events. eg addEventListener("click", doSomething) – the “type” equivalent there is the first argument, so type is “click”.
  • anyway: global store object. Gets updated by sending message via the dispatch function. This results in a new global object. Any component subscribed to it then rerenders with the updated data.

Note all the redux logic above is about 100 lines of very basic JS code. The react-redux bindings are more complicated, because React is a lot more complicated, but they’re basically just a wrapper over subscribe

1 Like

Hi Dan,

Thank you! I made the mistake of trying to run before I could walk, and let’s just say there’s been a lot of tripping involved :sweat_smile:

the unconventional naming of reducers seemed to be causing some other problems that I was unaware of, and fixing this has helped. Not to mention that I was too wedded to the idea of using the redux store that I didn’t plan out what I actually need to use it for. a lot of functionality I need doesn’t require me to interact with the store.

Thanks again for going out of your way to share knowledge :slight_smile:

I do need to read more into the underlying logic of React-redux, as my current understanding is still very much surface level.

Don

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.