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:
– 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.