DEV Community

Cicada0315
Cicada0315

Posted on • Updated on

Part 2: React-Redux

Let's actually make a new react app and go one by one to learn how to use redux with react.

Install

//create new react app
$ npm install -g create-react-app
$ create-react-app < APP-NAME >

//(optional) install Yarn
$ npm install --global yarn

//install redux  
$ npm install redux
$ npm install react-redux
Enter fullscreen mode Exit fullscreen mode

Useful tools

Redux DevTools,
https://github.com/zalmoxisus/redux-devtools-extension
React Developer Tools,
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi/related?hl=ko

Short Explanation for each library

import { createStore } from 'redux'

import { createStore } from 'redux'
createStore(reducer, [preloadedState], [enhancer])
Enter fullscreen mode Exit fullscreen mode

It creates store and returns that store. For more details for createStore or reducer use my first blog.
Link: https://dev.to/cicada0315/part-1-redux-1mi5

import { Provider } from 'react-redux'

Using component makes the Redux store available to any child components. In other word, it Enables to access store and dispatch actions from any component. You can simply think that it can pass store as props to the child components. Most of time the will render at the top level so that everyone can have access for store.

import { Provider } from 'react-redux'
  <Provider store={store}>
  </Provider>,
)
Enter fullscreen mode Exit fullscreen mode

import { connect } from 'react-redux'

To gain access to the store somewhere in our component we have to use this connect.

import { connect } from 'react-redux';
export default connect(mapStateToProps, mapDispatchToProps)(ComponentName);
Enter fullscreen mode Exit fullscreen mode

connect(): It's a function connects a React component to a Redux store.
mapStateToProps(aka mapState): It's a function takes a first argument called state, optionally a second argument called ownProps, and return a plain object which become a props for your component.
This function is passed in as the first argument to connect() and when connect() is run then it will passing in current state to the mapStateToProps.

const mapStateToProps = (state) => ({ characters: state.characters })
Enter fullscreen mode Exit fullscreen mode

mapDispatchToProps: It can be function, an object, or not supplied(null). This function expected to return an object. It is used for dispatching actions to the store.
This function is passed in as the second argument to connect().

const mapDispatchToProps = (dispatch) => {
  return {
    createCharacter: (character) => dispatch({ type: 'CREATE_CHARACTER', character }),
  }
}
Enter fullscreen mode Exit fullscreen mode

Good things to know!

Those three code is equivalent to each other

//three different code is equivalent to each other
export default connect(mapStateToProps, mapDispatchToProps)(ComponentName);
export default connect(mapStateToProps, { createCharacter })(ComponentName);
export default connect(state => ({ characters: state.characters }), { createCharacter })(ComponentName);
Enter fullscreen mode Exit fullscreen mode

Dive into coding!

Let's use what we've discussed above and make a simple app that can create characters and show list of created character.

Component tree (create folders and files under src)

  1. src/components/CharacterForm.js
  2. src/components/Character.js
  3. src/containers/Characters.js
  4. src/reducers/charactersReducer.js

alt Component_tree

Modify index.js

In index.js, I created store and take that store as provider argument to make it available to the child component of Apps.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux'; //add
import { createStore } from 'redux'; //add
import charactersReducer from "./reducers/charactersReducer"; //add

const store = createStore(charactersReducer); //add

ReactDOM.render(
  <React.StrictMode>
  <Provider store={store}>
    <App />
  </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();
Enter fullscreen mode Exit fullscreen mode

App.js

import './App.css';
import CharacterForm from './components/CharacterForm';
import Characters from './containers/Characters';

function App() {
  return (
    <div className="App">
      <h1>Welcome to Character Storage</h1>
      <CharacterForm />
      <Characters />
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

reducers/charactersReducer.js

For more information about reducer use my part 1: redux? blog.
link: https://dev.to/cicada0315/part-1-redux-1mi5

export default function charactersReducer(state={characters: []}, action) {
    switch(action.type){
        case "CREATE_CHARACTER":
            return {
                ...state,
                characters: [...state.characters, action.character]
            }
        default:
                return state
    }
}
Enter fullscreen mode Exit fullscreen mode

containers/Characters.js

To gain access to the store which contains characters array, I used connect() with first argument mapStateToProps here. Then, I used that characters array with map method to pass character as props to the child component character.

import React, { Component } from 'react'
import Character from '../components/Character'
import { connect } from 'react-redux';

class Characters extends Component {
    render() {
        const characters = this.props.characters.map(character => <Character character={character}/>);
        return (
            <div>
                <h1>Character list</h1>
               {characters}
            </div>
        );
    };
};

const mapStateToProps = (state) => {
    return{
        characters: state.characters
    };
};

export default connect(mapStateToProps)(Characters);
Enter fullscreen mode Exit fullscreen mode

components/Character.js

function Character(props){
    return (
      <div className="character">
        <h1>{props.character.name}</h1>
        <img src={props.character.image_url} alt={props.character.name} height="400" width="800"/>
        <h3>Description: {props.character.description}</h3>
      </div>
    );
}

export default Character;
Enter fullscreen mode Exit fullscreen mode

components/CharacterForm.js

To gain access to the store to dispatch action, I used connect() here too. Once the form is submitted. It will call the function handleSubmit which will dispatch createCharacter(this.state) which will pass the new created character in it as this.state as eventually add that new character to our state.characters array.

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

class CharacterForm extends Component{
    state={
        name: "",
        image_url: "",
        description: ""
    };

    handleChange=(e)=>{
        this.setState({
            [e.target.name]: e.target.value
        });
    };

    handleSubmit=(e)=>{
        e.preventDefault();
        this.props.createCharacter(this.state);
        this.setState({
            name: "",
            image_url: "",
            description: ""
        });
    }

    render(){
        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                <h1>Create New Character</h1>
                Name: <input type="text" name="name" value={this.state.name} onChange={this.handleChange}/><br />
                Image_url: <input type="url" name="image_url" value={this.state.image_url} onChange={this.handleChange}/><br />
                Description: <textarea name="description" value={this.state.description} onChange={this.handleChange}/><br />
                <input type = "submit" value = "Create New Character" />
                </form>
            </div>
        );
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        createCharacter: (character) => dispatch({ type: 'CREATE_CHARACTER', character }),
    }
  }

export default connect(null, mapDispatchToProps)(CharacterForm);
Enter fullscreen mode Exit fullscreen mode

We have everything here, so why don't you try in visual studio? You can use marvel website to add new characters.
link: https://www.marvel.com/characters

Reference

https://react-redux.js.org/using-react-redux/connect-mapstate
https://react-redux.js.org/using-react-redux/connect-mapdispatch

Top comments (0)