Adding Redux

branch-name:  
Click To Copy

 

In the previous tutorial we showed how to build custom Redux like store, which is a great way to understand how Redux work, but if we want a production grade app, we have to do more work on our custom Redux like store, since we cheated a bit.

For example our redux store emits setState no matter if the properties are actually changed or not which is ok for a demo app but for a big app with a lot of mapStateToProps it will have performance impact, so let’s not re-invent the wheel but use the Redux store instead.

yarn add redux react-redux

Since Redux like implementation that we did in the previous tutorial is very similar to the actual Redux, there are just a few changes that we need to do in order to start using Redux.

./src/components/App/index.js

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import PageLayout from '../../containers/PageLayout';
import { Provider } from 'react-redux';
import { createStore} from 'redux';
import reducers from '../../reducers';

import fetch from 'unfetch';


const store = createStore(reducers, {});

export default class App extends Component {

  render() {

    const GRAPHQL_URL = 'http://localhost:4001/graphql';
    const client = new ApolloClient({
      link: new HttpLink({ uri:  GRAPHQL_URL, fetch: fetch }),
      cache: new InMemoryCache()
    });  

    return (
      <Provider store={store}>
        <ApolloProvider client={client}>
          <Router>
            <Switch>
              <Route exact path="*" component={PageLayout} />    
            </Switch>
          </Router>
        </ApolloProvider>
      </Provider>
    );
  }
}

 what we just did:
– we removed our custom store implementation:

import Store from ‘../../store’;
import Reducers from ‘../../reducers’;
import Provider from ‘../../containers/Provider’;


– and replaced it with the Redux provider and create store (line 8 and 9)
– we called the Redux createStore (line 15)
– finally we wrapped the app with the new provider passing the store (line 28)

Next step is just cleaning up the code a bit.
– We renamed the index.js reducer to ./src/reducers/useer.js so we could keep each different reducer types in separate files and we added one main reducer ./src/reducers/index.js that will combine all other reducers. (in our case ./src/reducers/useer.js for now)

./src/reducers/user.js

const CHANGE_USERNAME = 'CHANGE_USERNAME';
const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE';

const initialState = {
  todos: [],
  userName: "No Name",
  editMode: false
};

  const reducer = (state = initialState, action) => {
    switch (action.type) {
      case CHANGE_USERNAME: {
        let newState = {...state};
        newState.userName = action.data;
        return newState;
      }

      case TOGGLE_EDIT_MODE: {
        let newState = {...state};
        newState.editMode = !newState.editMode;
        return newState; 
      }

      default:
        return state;
    }
  };

export default reducer;

 

./src/reducers/index.js

import { combineReducers } from 'redux';
import user from './user';

export default combineReducers({
  user
});

 

And the best part is that since our Redux like implementation was so similar to the actual Redux implementation, we have to make just two little changes:
– Replace import connect from '../../containers/Provider/connect'; with the Redux connect import { connect } from 'react-redux';
– And since we added combineReducers we have to add user property to access the users reducer.

./src/components/About/index.js ./src/components/Greetings/index.js.

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

const styles = require('./styles.scss');

const CHANGE_USERNAME = 'CHANGE_USERNAME';
const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE';

class Greetings extends Component {

  constructor(props) {
    super(props); 
  }
  

  doneEditUsername() {
    let newName = document.querySelector('#inputField').value;
    this.props.changeUserName(newName);
    this.props.toggleLogInPopup();
  }

  usernameChanged(el) {
    let newName = el.target.value;    
    this.props.changeUserName(newName);
  }

  onToggleEditMode() {
    this.props.toggleLogInPopup();
  }

  render() {
    let element = <h2 onClick={() =>{   this.onToggleEditMode()  }}>Hello:  {this.props.userName}</h2>;
    if(this.props.editMode)
      element = <h2>Type new name:<input type="text" id='inputField' value={this.props.userName} onChange={(el) => { this.usernameChanged(el);}} /><button onClick={() =>{ this.doneEditUsername() }}>done</button></h2>
    return (
      <div>
        <div className={styles.wrapper}>
          {element}
        </div>
      </div>);
  }
}

const mapStateToProps = ( storeState ) => {
  return {  
    userName: storeState.user.userName,
    editMode: storeState.user.editMode
  }
}

const mapDispatchToProps = dispatch => {
  return {
    toggleLogInPopup: () => {
      dispatch({type: TOGGLE_EDIT_MODE});
    },
    changeUserName: (userName) => {
      dispatch({type: CHANGE_USERNAME, data: userName});
    }
  }
};


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

 

./src/components/About/index.js ./src/components/About/index.js

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

const CHANGE_USERNAME = 'CHANGE_USERNAME';
class About extends Component {
  constructor(props) {
    super(props);
    this.state = {
      userName: this.props.userName,
    };    
  }
  handleChange() {
    const userName = document.querySelector('input[name=username]').value;
    this.setState( { userName: userName } );
    this.props.onEdit(userName);
  }
  render() {
    return (
      <div>
        <p>This is <input type="text" name="username" value={this.state.userName} onChange={() => { this.handleChange()}} /></p>
      </div>
    );
  }
}
//export default About;
const mapStateToProps = storeState => ({
  userName: storeState.user.userName
}
);
const mapDispatchToProps = dispatch => ({
  onEdit: (userName) => dispatch({
    type: CHANGE_USERNAME,
    data: userName
  })
});
const AboutContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(About);
export default AboutContainer;

 

And delete ./src/containers/Provider folder.

And now all custom Redux like implementation was replaced wit the actual Redux store. Give it a try and everything shoul;d work like before but using the actual Redux store.

 

branch-name:  
Click To Copy

 

Leave a Reply