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
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])
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>,
)
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);
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 })
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 }),
}
}
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);
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)
- src/components/CharacterForm.js
- src/components/Character.js
- src/containers/Characters.js
- src/reducers/charactersReducer.js
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();
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;
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
}
}
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);
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;
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);
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)