React-Redux Empty props in components

Hi guys :slight_smile:

My brain is dead and I’m making no progress so I’m asking for help :pray:
Just setting up React-Redux for the 25 & 5 timer and cannot figure out what’s going on with the state.

My React components should have access to the state within the react store, however there is nothing there when accessing props, just an empty object?
It looks as though state is never defined properly in the first place.

My brain is now hurting and honestly I’m now having trouble even following what I’ve just coded. If someone could cast there eye over it and point me in the right direction/give me some insite into what I’m missing here that would be wonderful!

Thank you,
Olly

edit A guess the link would be useful…

First of all, you don’t really need redux for the timer. It’s small enough that it is overkill. But if you’re doing it because you want to learn it better, I understand.

There are two ways to get props into a component: the standard React way of passing them in or using a higher order component (HOC, a function that takes in a component and returns a component). In React, order to hook into the redux store, you need to use an HOC. The react-redux package provides a function called connect. It takes in some data and then returns an HOC in which you can wrap your component.

const Count = props => <h3>{ props.myCount }>;

const mapStateToProps = state => ({ // this is the redux store/state, not your component state
  myCount: state.count, 
});

const reduxHOC = connect(mapStateToProps);
const ReduxConnectedCount = reduxHOC(Count);
// const ReduxConnectedCount = connect(mapStateToProps)(Count);

With that, that component would get that redux store/state value pumped into the component as props.

I hope this helps. Keep in mind that it’s going to get a little weird in something like codepen because everything is going to be in one file.

Now that I see your code, I suspect is a misunderstanding of react-redux. Connecting does not provide those values to every component in your app. Each component needs to be connected with its connect. Each component will need different things from the store and different dispatching function. You use connect and specific mstp and mdtp functions to tell connect what to give that specific function.

As I said, something like codepen makes this a little awkward because everything is in one file so you will quickly run into naming problems. In a more “typical” working environment, this is not a problem because each component gets its own file.

1 Like

What may help here: for learning to build apps, Codepen is not really going to be a good experience – it’s built for tiny sketches of functionality (for example a single CSS animation).

Codesandbox or Stackblitz (or Repl.it) are better options by far, and you’ll avoid shoving everything into a single file.

With Codesandbox/Stackblitz/{basically any of the VSCode-based online editors} you’re also going to have fairly powerful linting enabled out of the box, which is going to help a lot with getting stuff right in the first place.


I would also recommend looking at Redux toolkit. The documentation is really good, and it is recommended way to start new Redux projects.

Thank you @kevinSmith and @DanCouper for your replies :slight_smile:

I’ve started again in Codesandbox and indeed it is much much easier to get my head around now the files are organised. As you mentioned Kevin, I am only including Redux because I want to learn it.

I’d be grateful if you could look over my sandbox and tell me if I’m headed in the right direction, it certainly seems to make more sense to me now.
I’ve only connected the ‘Work’ component to the Redux store for the moment to see if things are working.
Everything was going smoothly until I made this connection and now I’m getting a ModuleNotFoundError - Could not find module in path: ‘./timerButtonTypes’ relative to ‘/src/redux/work/workReducer.js’

I can’t see anything wrong with my code, but obviously I’m missing something!

Your structure is

redux
  |_ work
      |_ workReducer.js
  |_ timerButtonTypes.js

So path from workReducer to timerButtonTypes needs to be ../timerButtonTypes, missing period (you’re moving up a directory).

One of the things re using Codesandbox – that import declaration should have a red squiggle under it when it’s wrong I think? Keep an eye on those, as it’s the IDE highlighting errors it can immediately see.

1 Like

Thanks @DanCouper, I’m making slow progress now.
Just trying to write a function for the main 25 timer and for whatever reason it’s not working as it should…it seems as though the component is not updating with new props as the store state changes?
Maybe you could point me in the right direction :sweat_smile:

Ok, i’ll have a look at this. It’s getting close but there are a few things that jump out straightaway: the stop doesn’t actually stop anything, the seconds count is a an issue (it’s going negative), and the +/- buttons allow setting a negative time.

I’ll dig through and see how it can be fixed.

Thanks Dan. Yes I was going to worry about the +/- buttons setting a negative time and the stop button later on (but if you can help that’s great).
I’m mostly concerned about the seconds going negative.
I’ve written a basic recursive countdown function which should dispatch an action every second decrementing the seconds state by 1. When seconds reach 0, another dispatch should decrement minutes by 1, then another dispatch resets seconds to 60 and then the function calls itself. This should continue until minutes reach -1.
The logic for the function is fine I’m sure (tell me if I’m wrong!) but I think my component and the store are not communicating properly?
The countdown begins, but the if statement never catches seconds when they reach 0. The countdown continues into minus numbers.
If I console log the props, I can see that although the countdown timer is running and counting down the seconds…the seconds prop is never updated within the component.
Actually just checked again it seems to update store if I take a log from outside of the runTimer function, but the function seems to be shielded from store updates?

So basically I would not do the logic relating to the 60 seconds, dispatching every second then dispatch for minutes and so on. Just have the remaining time in seconds, that way it just decrements to 0 (obvs need to fix logic there to prevent it going below zero). So as an example, here is a component that accepts the remaining seconds as a prop and renders minutes and seconds for the user:

This is just an example: what it can use instead of a remainingSeconds prop is the state of the redux store

Thanks Dan. That does look like a better solution, thank you.
However I’d still like to know why my main timer is going into minus numbers? I’ve built the timer before in vanilla javascript with no issues so I know the logic is sounds (perhaps not so elegant!). My main question is why does it not work in React and Redux? Otherwise presumably I’ve got the same issue if I implement your solution.
The logic is within the startStop component.

EDIT @DanCouper

function StartStop(props) {
  function runTimer() {
    if (props.workLengthMins <= -1) {
      return;
    }
    props.start();
    props.decrementSecs();
    console.log(props.workLengthSecs)
    if (props.workLengthSecs < 0) {
      props.decrementMins();
      props.secsToSixty();
      runTimer();
    } else {
      setTimeout(runTimer, 1000);
    }
  }
  console.log(props.workLengthSecs)

The console log outside of the startStop function shows the state decrementing by one every second as you’d expect.
But the console log inside of the startStop function (where it matters) makes the same log every time, does not count down. So the if statement that decrements the minutes and resets the seconds is never triggered. The startStop function does not seem to have access to the redux store state as it’s changing. Do you know why this is?
The render is seeing the same state as the console log outside of the StartStop function, therefor is rendering the seconds endlessly counting down.

I fear the reason is that you have created a closure where props never reads the actual “updated” props, but just the original one passed as arguments, so your conditions never match.

Yes yes yes. I think you’re exactly right. But how have I done this, and how do I correct it? :grimacing:
Or is it just a fact that functions inside of components don’t have access to updated props?

Right, the issue you’re going top have here is that React doesn’t work like this. You can kinda force it to work in kinda the way you’re doing it, but I think you’re on a hiding to nothing. I would rethink how you’re structuring things a bit here; the way it’s set up is too complex for one thing (definitely switch to using seconds asap, because the logic for handling switching between minutes and seconds is making it very difficult to debug).

So first of all, try to do this just using state/props, rather than Redux. There is absolutely nothing wrong with using Redux, but it would be helpful I think to do it without at first.

To help (hopefully), I’ve added an implementation of a simple countdown with start/stop functionality + the cycles to my Sandbox. I’ve set the time really low as well (I’d advise always doing this with yours while you’re building it else it’ll drive you up the wall!)

I can walk you through anything you don’t understand (and there’s a long, but very good walkthrough of what useEffect does here). And then can replace the internal state with Redux’ external state

1 Like

Yes they do, and that’s exactly what @DanCouper suggested you in the post above:
via the useEffect hook

Thank you guys! :slight_smile:
I took your advice @DanCouper and started again. I think I understand better now how React works, and when and why to use ‘useEffect’/
It seems as though most of the state in most applications will ultimately have to be lifted up to ‘App’ level so all children components get access to the state they need. This makes the App component pretty messy no!?
I’ll fork it now and start adding Redux into the mix.
Where I’m at now…

edit
Added a progress bar, think it looks cool :slight_smile:
Not very mobile friendly yet but I’m getting sidetracked…