Search code examples
javascriptreactjsreact-reduxreact-router-v4redux-thunk

Redux store not connected


I am developing a Reactjs web application from scratch and encountered a tricky situation which i need help with. Whenever i navigate away from a particular url and navigate back, my redux store does not seem to be connected.

routes.js

const RouteList = () => (
  <main>
    <Switch>
      <Route path="/abc/" exact component={withRouter(HomePage)} />
      <Route path="/abc/xyz" exact component={withRouter(XYZPage)} />
      <Redirect from="/" to="/abc/" />
      <Route component={Error} />
     </Switch>
   </main>
);
export default RouteList;

App.js

class  App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render () {
    return (
      <Router history={browserHistory}>
        <div>
          <Header />
          <RouteList />
          <Footer />
        </div>
      </Router>
    );
  }
}

export default App;

Header.js

const Header = () => {
  return (
    <Navbar expand="md">
      <NavbarBrand tag={NavLink} to="/">
        <img src={brandImage} style={{marginRight: "0", width: "40px", height: "40px"}} /><strong style={{color: "#457B9D"}} >Datum</strong>
      </NavbarBrand>

      <Nav className="mr-auto" navbar>
        <NavItem>
          <NavLink className="nav-link" to={"/abc/xyz"} >XYZ</NavLink>
        </NavItem>
      </Nav>
    </Navbar>
  );
};

export default withRouter(Header);

When i hit the NavLink which will take me to url: /"abc/xyz", it will take me to XYZPage.js

XYZPage.js

class XYZPage extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.state = {
      activeTab: "1"
    };

    this.toggle = this.toggle.bind(this);
  }

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      });
    }
  }
  render () {
    return (
      <main>
        <div className="container-fluid pt-3">
          <Nav tabs>
            <NavItem>
              <NavLink
                className={classnames({active: this.state.activeTab === "1"})}
                onClick={() => {this.toggle("1"); }} >
                AAA
              </NavLink>
            </NavItem>
            <NavItem>
              <NavLink
                className={classnames({active: this.state.activeTab === "2"})}
                onClick={() => {this.toggle("2"); }} >
                BBB
              </NavLink>
            </NavItem>
            <NavItem>
              <NavLink
                className={classnames({active: this.state.activeTab === "3"})}
                onClick={() => {this.toggle("3"); }} >
                CCC
              </NavLink>
            </NavItem>
          </Nav>
          <TabContent activeTab={this.state.activeTab}>
            <TabPane tabId="1">
              <Row>
                <AAAPAge/>
              </Row>
            </TabPane>
            <TabPane tabId="2">
              <Row>
                <BBBPage/>
              </Row>
            </TabPane>
            <TabPane tabId="3">
              <Row>
                <CCCPage/>
              </Row>
            </TabPane>
          </TabContent>
        </div>
      </main>
    );
  }
}

export default withRouter(XYZPage);

Each of the AAAPage, BBBPage & CCCPage are components which needs to have some pre-populated dropdowns which i declared in my index.js below:

index.js

const store = configureStore();
store.dispatch(loadAAA());
store.dispatch(loadBBB());
store.dispatch(loadCCC());

render((
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>
), document.getElementById('app'));

loadAAA, loadBBB & loadCCC are all thunks

The configureStore() method is as such:

export default function configureStore(initialState) {
  return createStore(
    rootReducer,
    initialState,
    composeWithDevTools(
      applyMiddleware(thunk, reduxImmutableStateInvariant()),
    )
  );
}

To shorten this post i give a sample of my AAAPage as the others are of similar structure:

AAAPage.js:

class AAAPage extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {...};
  }

  componentWillReceiveProps(nextProps) {...}

  render() {
    [...]
    return (
      <Container fluid>
        <Row>
          <AAAInputForm
            // Data from Store is passed here 
          />
        </Row>
        {ChildComponent}
      </Container>
    );
  }
}

AAAPage.propTypes = {
  DATA: PropTypes.array
};

function mapStateToProps(state, ownProps) {
  let DATA = [];
  if (state.AAAReducer.length > 0) {
    DATA = state.AAAReducer;
  }

  return {
    DATA: DATA
  };
}

export default withRouter(connect(mapStateToProps)(AAAPage));

AAAReducer.js:

export default function AAAReducer(state=initialState.AAAList, action) {
  switch(action.type) {
    case types.LOAD_AAA_SUCCESS:
      return action.AAAList;

    default:
      return state;
  }
}

AAAAction.js:

export function loadAAASuccess(AAAList) {
  return {
    type: types.LOAD_AAA_SUCCESS,
    AAAList: AAAlList
  };
}

// thunk
export function loadAAA() {
  // A thunk will always return a function that accepts a dispatch
  return function(dispatch) {
    return apiCall("ALL").then(response => {
      dispatch(loadAAASuccess(response.data.AAA));
    }).catch(error => {
      throw(error);
    });
  };
}

initialState.js:

export default {
  AAAList: [],
  BBBList: [],
  CCCList: []
};

At this point i believe i provided enough background to my code. I followed tutorials when designing this redux store and I am not sure why when i navigate from "/abc/xyz" to "/abc" and back, or when i navigate to "/abc/xyz" from "/abc", my stores are empty although i called the loadAAA() method at my index.js. All the other pages are affected as well. However, when i hit "/abc/xyz" directly, my stores are connected and my dropdowns are populated. What is happening? Is it because of my lifecycle methods?

I am using react v15.6.2, redux v3.7.2 & redux-thunk v2.3.0.

Thanks for the guidance.


Solution

  • You only call loadAAA at the top level of index.js, which only executes once when your page loads. If you want to dispatch it every time your XYZPage page renders, put in XYZ's componentDidMount