1
votes

I'm new to Redux, using it with React, and am in need of some help. I have a menu that when a menu item is clicked, another component needs to update some copy. I'm able to dispatch an action and have the store update. However, I can't get the child component (HeroText) to render the new store value in the store.subscribe method when the store values change. Please help and thanks!

import React, { Component } from "react";
import ReactDOM from "react-dom";
import HeroText from "../presentational/HeroText.jsx";
import bgImage from "../../../images/forest_fog.jpg";
import AnantaNavbar from "../presentational/AnantaNavbar.jsx";
import '../../../scss/hero.scss';
import store from '../../store/index';
import { connect } from "react-redux";


const mapStateToProps = state => {
    return {
        contact: state.contact,
        heroText: state.heroText
    }
}

class HeroContainer extends Component {
  constructor(props)
  {
    super(props);

    this.state = store.getState();

    store.subscribe(() => {
        console.log(store.getState().heroText);
        this.setState({
            heroText: store.getState().heroText,
        })
    })
  }

  render()
  {
    return (
        <div id="hero-container" style={{backgroundImage: ("url(" + bgImage + ")") || ""}}>
            <div className="container">
                <HeroText text={this.props.heroText}>
                    Welcome back {this.props.contact.full_name}
                </HeroText>

                <AnantaNavbar></AnantaNavbar>
            </div>
        </div>
    );
  }
}

export default connect(mapStateToProps)(HeroContainer);

UPDATE Below is my parent App Container with Provider

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Navbar, NavbarBrand, NavbarNav, NavbarToggler, Collapse, NavItem, NavLink, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'react-bootstrap';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import LoginContainer from '../../../js/components/container/LoginContainer.jsx';
import DashboardContainer from '../../../js/components/container/DashboardContainer.jsx';
import HomeContainer from '../../../js/components/container/DashboardContainer.jsx';
import ProfileContainer from '../../../js/components/container/ProfileContainer.jsx';
import HeroContainer from "./HeroContainer.jsx";
import '../../../scss/globals.scss';
import logo from '../../../images/logo1.png';
import { Provider } from 'react-redux';
import store from '../../store/index';

const Router = () => (
    <BrowserRouter>
        <Switch>
            <Route exact path="/" component={LoginContainer} />
            <Route exact path="/login" component={LoginContainer} />
            <Route exact path="/home" component={HomeContainer} />
            <React.Fragment>
                <HeroContainer />
                <Route path="/dashboard" component={DashboardContainer} />
                <Route path="/profile" component={ProfileContainer} />
            </React.Fragment>
        </Switch>
    </BrowserRouter>
);

class AppContainer extends Component {
  constructor(props)
  {
    super(props);
    this.state = {

    };
  }

  componentDidMount()
  {

  }

  render()
  {
    return (
        <div>
            <Provider store={store}>
                <Router></Router>
            </Provider>
        </div>
    );
  }
}

export default AppContainer;

The default heroText in the store says "DASHBOARD". When a menu item is clicked, in this case a link to /profile, the heroText should update to "PROFILE" after updating the store.

enter image description here

You can see in the console that the store is changing, but the "DASHBOARD" copy is not reflecting.

RESOLVED I got this working with the code below. Thanks for all the help!

import React, { Component } from "react";
import ReactDOM from "react-dom";
import HeroText from "../presentational/HeroText.jsx";
import bgImage from "../../../images/forest_fog.jpg";
import AnantaNavbar from "../presentational/AnantaNavbar.jsx";
import '../../../scss/hero.scss';
import store from '../../store/index';
import { connect } from "react-redux";


const mapStateToProps = state => {
    return {
        contact: state.contact,
        heroText: state.heroText
    }
}

class HeroContainer extends Component {
  constructor(props)
  {
    super(props);
  }

  render()
  {
    return (
        <div id="hero-container" style={{backgroundImage: ("url(" + bgImage + ")") || ""}}>
            <div className="container">
                <HeroText text={store.getState().heroText}>
                    Welcome back {store.getState().contact.full_name}
                </HeroText>

                <AnantaNavbar></AnantaNavbar>
            </div>
        </div>
    );
  }
}

export default connect(mapStateToProps)(HeroContainer);
1
You're using subscribe when Connect already does this for you, also you're using props.heroText when you're storing it on state - lux

1 Answers

0
votes

Since you are trying to get state from Redux, there's no pointing in keeping it in local state. Plus, you don't need to use store.getState, connect already does that for you.

const mapStateToProps = state => {
  return {
    contact: state.contact,
    heroText: state.heroText
  }
}

class HeroContainer extends Component {
  render() {
    return (
      <div id="hero-container" style={{backgroundImage: ("url(" + bgImage + ")") || ""}}>
        <div className="container">
          <HeroText text={this.props.heroText}>
            Welcome back {this.props.contact.full_name}
          </HeroText>

          <AnantaNavbar></AnantaNavbar>
        </div>
      </div>
    );
  }
}

export default connect(mapStateToProps)(HeroContainer);

You also need to make sure that your app is wrapped in a provider, like this:

  <Provider store={store}>
    <App />
  </Provider>