1

I am building an app prototype that essentially simulates ecommerce. I have components that each have different items that can be added to a cart(below I just show an example of how one would basically work). These components are accessed via different routes using the react-router. There is a header component that displays the number of items currently in the cart. The header gets the number of items in the cart from the state in the redux store. However, if I navigate to a new route, the store goes back to the default state. I need the the store to keep its state when a new route is navigated to. For example, if I go to the ShoppingPage, add an item to the cart, and then go back to the Home page, I need the cart to still have an item in it.

actions.js

export const actionTypes = Object.freeze({
    UPDATE_CART: Symbol('UPDATE_CART'),
});

export const updateCart = (payload) => {
    return {
        type: actionTypes.UPDATE_CART,
        payload,
    };
};

export default actionTypes;

reducer.js

import actions from './actions';

export const INITIAL_STATE = {
    cart: [],
};

export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case actions.UPDATE_CART: {
            return {
                ...state,
                cart: action.payload,
            };
        }
        default: {
            return state;
        }
    };
};

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer, { INITIAL_STATE } from './reducer';

const store = createStore(reducer, INITIAL_STATE);
console.log(store.getState());
ReactDOM.render(
<Provider store ={store}>
    <BrowserRouter>
        <App />
    </BrowserRouter>
</Provider>
, document.getElementById('root'));

serviceWorker.unregister();

ShoppingPage.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateCart } from './actions';

class ShoppingPage extends Component {
    addToCart = () => {
        const cart = [...this.props.cart];
        cart.push('new item');
        this.props.modifyCart(cart);

    render() {
        return(
            <div>
                <button onClick={addToCart}>
                    Add To Cart
                </button>
            </div>
        )
    }
}

const mapDispatchToProps = dispatch => ({
    modifyCart: payload => dispatch(updateCart(payload)),
});

const mapStateToProps = state => ({
    cart: state.cart,
});

export default connect(
    mapStateToProps,
    mapDispatchToProps,
)(ShoppingPage);

Home.js

import React, { Component } from 'react';
import { ListGroup, ListGroupItem } from 'reactstrap';

class Home extends Component {
    render() {
        return(
            <div>
                <ListGroup>
                    <ListGroupItem><a href='/ShoppingPage'>ShoppingPage</a></ListGroupItem>
            </div>
        )
    }
}

export default Home;

Header.js

import React, { Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import { connect } from 'react-redux';

class Header extends Component {
    render() {
        return(
            <Navbar sticky='top' className='nav'>
                <NavbarBrand href='/'>Online Shopping</NavbarBrand>
                    <span>{'Items in Cart: '}{this.props.cart.length}</span>
            </Navbar>
        )
    }
}

const mapStateToProps = state => ({
    cart: state.cart,
});

export default connect(
    mapStateToProps
)(Header);

Routes.js

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './Home';
import ShoppingPage from './ShoppingPage';

const Routes = () => (
    <Switch>
        <Route exact path='/' component={Home} />
        <Route exact path='/ShoppingPage' component={ShoppingPage} />
    </Switch>
);

export default Routes;

App.js

import React from 'react';
import Routes from './Routes';
import Header from './Header';

function App() {
  return (
    <div>
        <Header />
        <Routes />
    </div>
  );
}

export default App;
2
  • have you checked out connected-react-router? Commented Nov 20, 2019 at 1:48
  • @Dennis Murphy Don't do this: cart.push('new item'); Instead, only pass the "new item" to the action. It's the job of the reducer to push it into the array. The reducer knows what data structure the cart is comprised of and maintains it. In the React components you generally only read props, never write/mutate them. Commented Nov 20, 2019 at 7:31

2 Answers 2

1

What's likely happening is that during navigation the web app "reloads" again (which is wiping the redux state). In order to navigate with react router you want to look at <Link>.

For example,

Home.js

<a href='/ShoppingPage'>ShoppingPage</a>

should be changed to:

<Link to="/ShoppingPage">ShoppingPage</Link>
Sign up to request clarification or add additional context in comments.

3 Comments

Frak this was an easy one lol. I didn't even think to look at that
Thank you so much. You're a saint :D
Is this <Link> the one from react-router? It doesn't seem to work outside a <Router>.
0

Also I believe that <Header /> needs to be a child of <Routes /> in order for us to use react router navigation. I understand the reason for having it as a separate component and perhaps someone has a cleaner solution, but you can import it for each route component and that should work.

<Router>
  <Routes>
    <Route path="/" element={<Dashboard />} />
    ...
  </Routes>
</Router>
// For the dashboard component
const Dashboard = () => {
  ...
  return (
    <div>
     <Header />
     <DashboardContent />
    </div>
  )
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.