0
votes

Button.js component

    import React from "react"
import "../styles/button.scss"
import store from "../index"

class Button extends React.Component {
    constructor(props) {
        super(props)
        this.buttonTextChanger()
    }

    buttonTextChanger() {
        this.buttonText = "MainPage" === this.props.state.page ? "Az adatokhoz" : "A főoldalra"
    }


    logger = (actualPage) => {
        this.props.onButtonClick(actualPage)
        console.log("state from store before textchange", store.getState())
        console.log("state from props before textchange", this.props.state)
        this.buttonTextChanger()
    }

    render() {
        return (
            <div id="container">
                <button className="learn-more" onClick = {() => this.logger(this.props.state.page)}>
                    <span className="circle" aria-hidden="true">
                    <span className="icon arrow"></span>
                    </span>
                    <span className="button-text">{this.buttonText}</span>
                </button>
            </div>
        )
    }
}

export default Button

My problem is that the component's props don't seem to be updated with the redux store. The redux store updates to the correct value after the onClick function runs, mapStateToProps also runs with the correct state and still after these if I try to log the state from the prop I get the old value. If I do the same log in the render function before returning the JSX I get the correct state from props and I can't get my head around why it isn't updated immediately after the redux store is. So if I modify the code to the following it works as expected:

logger = (actualPage) => {
        this.props.onButtonClick(actualPage)
        console.log("state from store before textchange", store.getState())

    }

    render() { 
        console.log("state from props before textchange", this.props.state)
        this.buttonTextChanger()  
        return (
            <div id="container">
                <button className="learn-more" onClick = {() => this.logger(this.props.state.page)}>
                    <span className="circle" aria-hidden="true">
                    <span className="icon arrow"></span>
                    </span>
                    <span className="button-text">{this.buttonText}</span>
                </button>
            </div>
        )
    }
}

Reducer function

import { combineReducers } from "redux"

export const changePageReducer = (state = {page : "MainPage"}, action) => {
    if (action.type === "CHANGE_PAGE")
        if (action.payload !== state.page) {
            return action.payload
        }

    return state.page
}

export const combinedReducers = combineReducers({page : changePageReducer})

Button container

import { connect } from "react-redux"
import Button from "../components/Button"
import changePage from "../actions/changePage"

const mapStateToProps = (state) => {
    console.log("az injectelt state", state)
    return {state}
} 

const mapDispatchToProps = (dispatch) => {
    return {
        onButtonClick : (page) => {
            switch (page) {
                case "MainPage":
                    dispatch(changePage("DataPage"))
                    break
                case "DataPage":
                    dispatch(changePage("MainPage"))
                    break
                default:
                    dispatch(changePage("MainPage"))
            }
        }
    }
}

const ChangePageContainer = connect(mapStateToProps, mapDispatchToProps)(Button)

export default ChangePageContainer

But I'd like to extract the buttonTextChanger() function call from the render function and call it on click.

TLDR: the problem:

logger = (actualPage) => {
  console.log("prop state value before dispatch", this.props.state)
  console.log("store value before dispatch", store.getState())
  this.props.onButtonClick(actualPage)
  console.log("prop state value after dispatch", this.props.state)
  console.log("store value after dispatch", store.getState())
}

also there is a console.log in the mapStateToProps function to see the state that gets passed to props. This yields:

prop state value before dispatch {page: "MainPage"}
store value before dispatch {page: "MainPage"}
state when mapStateToProps function called {page: "DataPage"}
store value after dispatch {page: "DataPage"}
prop state value after dispatch {page: "MainPage"}

So the prop doesn't get updated.

1
Can you post your reducer function also?Ajin Kabeer
for that purpose you need to use componentWillReceivePropsTapan Dave
so basically you need to play with data that you are updating to store right ?Tapan Dave
yes, I updated the question so it contains more informationb.zsombor

1 Answers

2
votes

So, you can't wrap your head around why this.props.state isn't updated even after calling the dispatcher?

You see, Redux is entirely built on functional programming, and now with hooks, React is also fully moving towards Functional Programming, hell even JavaScript was originally built as Functional Programming.

One thing that's entirely different from OOP and the coolest thing too is that, There are no mutations in Functional Programming. There isn't. Vanilla Redux is fully FP having only pure functions - functions without side effects. That's why you need Redux Saga or other library for making API calls - unpure functions.

Cutting to the chase,

logger = (actualPage) => {
  // here,
  // actualPage => new value
  // this.props.state => old value
  // they will remain as such throughout this function


  console.log("prop state value before dispatch", this.props.state)
  console.log("store value before dispatch", store.getState())
  this.props.onButtonClick(actualPage)

  // even if you update the Redux Store, 
  // `this.props.state` will still have the old value
  // since this value will not be mutated

  console.log("prop state value after dispatch", this.props.state)
  console.log("store value after dispatch", store.getState())
}

Then what about store.getState(), their values are updated, you may ask.

Notice how it's not store.getState but instead store.getState()? Yes it is a function instead of a value. Whenever you call them, they return the latest value and there isn't any mutations in them. In your case, the second time you are calling after dispatching the action, hence you get the latest value. It's not mutation, store.getState() just grabbed the latest value there is and returned it to you. logger() will get the new values once that first call is over, then the cycle repeats again.

Action dispatchers will create a new state from the old state, that's why you can time travel in Redux DevTools.

Again recapping

logger = (actualPage) => {
  // old value
  console.log("prop state value before dispatch", this.props.state)
  // old value
  console.log("store value before dispatch", store.getState())

  // new value
  this.props.onButtonClick(actualPage)
  // redux would be updated with new values by now

  // but still old value - no mutation
  console.log("prop state value after dispatch", this.props.state)
  // new value
  console.log("store value after dispatch", store.getState())
}