Let's take the function of adding to do list as an example:
In order to develop specifications, we should adopt the fragment writing method of reducer, that is, one type of data and one module
Write specifications without react Redux
Production and installation redux
yarn add redux
Create a store directory under the src directory
Create index.js and reducer.js
index.js
import { createStore } from 'redux' import reducer from './reducer' const store = new createStore ( reducer ) export default store
reducer.js
import { combineReducers } from 'redux' const reducer = combineReducers({ }) export default reducer
Create todos folder, and then create
state.js
const state = { todos : [ { id : 1, text : 'sleep' }, { id : 2, text : 'having dinner' }, { id : 3, text : 'Beat beans' } ] } export default state
type.js
export const ADD_TODOS = 'ADD_TODOS'
actionCreators.js
import * as type from './type' import store from '../index' const actionCreators = { addTodos( value ){ let action = { type : type.ADD_TODOS, payload : value } store.dispatch( action )//It is equivalent to the commit in vue and sends the action to reducer } } export default actionCreators
reducer.js
import * as type from './type' import state from './state' const reducer = ( previousState = state,action) => {//Assign state to previousState as the initial value let newState = {//Structure to the new state, and then operate on the new state ...previousState } switch (action.type) { case type.ADD_TODOS: newState.todos.push({ id : newState.todos.length + 1, text : action.payload }) break; default: break; } console.log(newState) return newState } export default reducer
After these are completed, we need to get the reducer under todos folder to the reducer under src for use, that is, change the reducer.js file under src directory to this
import { combineReducers } from 'redux' import todos from './todos/reducer' const reducer = combineReducers({ todos }) export default reducer
Such a Redux is completed, and then the component is created and then called store.
Create pages directory, todos directory and todo components under src directory
In the component, we call the method in store. In the two file, TodoInput is to increase the task, so we should call the activeCreators method in TodoInput to perform the task.
TodoInput.js
import React,{ Component,Fragment } from 'react' import actionCreators from '../../store/todos/actionCreators'//Introducing actionCreators class TodoInput extends Component { addTodos = (e) => { let keyCode = e.keyCode let value = e.target.value if ( keyCode === 13 ){ actionCreators.addTodos(value)//Call the method in actionCreators } } render () { return ( <Fragment> <input onKeyUp={ this.addTodos } type="text"></input> </Fragment> ) } } export default TodoInput
TodoContent.js
TodoContent shows the data. Here are several key points. The view update in Redux needs to be completed by store.subscribe,
Knowledge points:
- List rendering method
- How to make the method in actionCreators perform post view update
import React,{ Component,Fragment } from 'react' import store from '../../store/index' const Item = (props) => { return ( <li>{ props.item.text }</li> ) } class TodoContent extends Component { constructor () { super() this.state={ todos : store.getState().todos.todos//The name in reducer to the data name in state in todos } } renderItem = () => {//Best list rendering method return this.state.todos.map( (item,index)=>{ return <Item item={ item } key={ item.id }></Item> }) } componentDidMount(){//Method of view update store.subscribe(()=>{ this.setState({ todos : store.getState().todos.todos }) }) } render () { return ( <Fragment> <ul> { this.renderItem() } </ul> </Fragment> ) } } export default TodoContent
index.js
import TodoInput from './TodoInput' import TodoContent from './TodoContent' export { TodoInput,TodoContent }
Use react Redux specification
Knowledge points involved in using react Redux
Core concept
Container component (smart component), UI component (puppet component)
React Redux thinks that if a component wants to use the data in the store or the method of actionCreator, we should change it to the way that the container component wraps the UI component
Among them, the container component is responsible for connecting the store and passing the state and method to the UI component. The UI component obtains these APIs from the properties and uses them
Moreover, there is no need to worry that container components can be generated according to UI components
Core API
Provider , connect(mapStateToProps,mapDistpatchToProps)
The Provider is responsible for passing store related APIs into all internal container components
connect is responsible for generating container components according to UI components
Usage and details
We modified it according to the previous case that did not use react redux
Install react Redux in production environment
yarn add react-redux
You need to put the Provider component on the outermost layer of the component and pass in the store for it
Then wrap the App component in the main entry file with the Provider component. Note that the Provider component comes from react Redux, and the store needs to be introduced to assign a value to the Provider
import store from './store' import { Provider } from 'react-redux' ReactDOM.render( <Provider store = { store }> <App /> </Provider> , document.getElementById('root'));
Use connect to change components that need to use store related APIs into container components and nested UI components
connect The return value of the method is a function that receives UI Component will return a container component, which has been nested inside UI assembly Provider Components will utilize context The context sets itself in the attribute store Passed to its own child component, and the container component gets context above store dependent api We can connect Passed in function mapStateToProps/mapDispatchToProps Parameters to control container components UI The process by which a component passes properties *mapStateToProps Role of*: take store Medium state Pass to UI On the properties of the component Value is a function that receives store Medium state What is the return value, UI What are the properties of the component And because it's already done in the container component store.subscribe So once store When the state changes in the container, the container component will know immediately and give it again UI Component incoming new data *problem*: Pit? ours store The data in is being updated, but the data in the container component inside props Do not update Analysis: start from the component, check the code we wrote, and find that the code is OK. At this time, we have to check the data source, and the data source comes from reducer , Then output the data. It is found that the data is rendered three times. Two times, when a component is created, the data is generated from nothing, and another time, it is generated by the container component. We will send our data later new_state Repeated initialization, Solution: let newState = {...previousState} mapDispatchToProps Role of: Can be used to dispatch Some methods passed to UI On components Value is a function that receives store Medium dispatch Go back up, UI What are the properties of the component This time actionCreator It becomes pure, just create action,dispatch action Your actions can be put mapDispatchToProps In other words, in mapDispatchToProps to UI In the function passed by the component actionCreator Create good action to dispatch go But the advantages of this writing are uncomfortable because actionCreator There is a method in the, which needs to be mapDispatchToProps Write another method in So it can be used redux Medium bindActionCreators take actionCreator The method in is put directly into UI Component and return it action Give direct dispatch go Because it is used in components store The status in is also used actionCreator This component introduces a lot of things. In fact, we can take these logic packages out
import React,{ Component,Fragment } from 'react' import actionCreators from '../../store/todos/actionCreators' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' const Item = (props) => { return ( <li>{ props.item.text }</li> ) } class TodoContent extends Component { renderItem = () => { return this.props.todos.todos.map( (item,index)=>{ return <Item item={ item } key={ item.id }></Item> }) } addTodos = (e) => { let keyCode = e.keyCode let value = e.target.value if ( keyCode === 13 ){ this.props.addTodos(value) } } render () { console.log('render') return ( <Fragment> <input onKeyUp={ this.addTodos } type="text"></input> <ul> { this.renderItem() } </ul> </Fragment> ) } } // Const mapstatetoprops = (state) = > {/ / the first connect method // // return state.todos // return { // todos : state.todos // } // } // const mapDispatchToProps = ( dispatch ) => { // console.log(dispatch) // return bindActionCreators ( actionCreators,dispatch ) // } // export default connect(mapStateToProps,mapDispatchToProps)(TodoContent) export default connect(//The second connect method state => state, dispatch => bindActionCreators(actionCreators,dispatch) )(TodoContent)
Note: if the method and data are used in different components, for example, the input box and list display of the above case are not in the same component, the list display component can only import
export default connect( state => state )(TodoContent)
Task input components need to be used
export default connect( state => state, dispatch => bindActionCreators(actionCreators,dispatch) )(TodoInput)
Modify the actionCreators.js file under stor
import * as type from './type' const actionCreators = { addTodos( value ){ let action = { type : type.ADD_TODOS, payload : value } return action } } export default actionCreators
The change is from store.dispatch(action) to return action, because our dispatch function is now left to the component
Asynchronous tool Redux thunk of react Redux
Install Redux thunk
yarn add redux-thunk
Use details
The scenario fetch requests a piece of data (the data request is asynchronous), takes the todo data as the data, and then continues according to the above react redux
An important concept of Redux thunk is the application middleware
We mainly modify index.js in the store directory
import { createStore,applyMiddleware } from 'redux' import thunk from 'redux-thunk' import reducer from './reducer' const store = new createStore( reducer,applyMiddleware(thunk) ) export default store
The index.js in the new store is like this
Data requests are placed in actionCreators.js
The expression of dispatch (action) in the data request here is equivalent to return action, which will also be changed in use
import * as type from './type' const actionCreators = { getData(){ return dispatch =>{ fetch('/data.json') .then(res=>res.json()) .then(data=>{ console.log('datad',data) let action = { type : type.GET_DATA, payload : data } dispatch( action ) }) .catch(error=>console.log(error)) } }, addTodos(value){ let action = { type : type.ADD_TODOS, payload : value } return action } } export default actionCreators
Modify it in reducer.js
import * as type from './type' import state from './state' const reducer = (previousState = state,action)=>{ let newState = { ...previousState } switch(action.type){ case type.GET_DATA: newState.todos=action.payload break; case type.ADD_TODOS: newState.todos.push({ id : newState.todos.length + 1, text : action.payload }) break; default: break; } console.log(newState) return newState } export default reducer
call
TodoInput.js
import React,{ Component,Fragment } from 'react' // import { connect } from 'react-redux' // import { bindActionCreators } from 'redux' // import actionCreators from '../../store/todos/actionCreators' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import actionCreators from '../../store/todos/actionCreators' class TodoInput extends Component { addTodos = (e)=>{ let keyCode = e.keyCode let value = e.target.value if (keyCode === 13){ this.props.addTodos(value) } } getData=()=>{ this.props.getData() } render() { return ( <Fragment> <button onClick={ this.getData }>get data</button> <input onKeyUp={ this.addTodos } type="text" placeholder="Please enter big Baba"></input> </Fragment> ) } } export default connect( state => state,//Must add dispatch => bindActionCreators( actionCreators,dispatch ) )(TodoInput)
There is a special point. If a component only needs state, it can not need the dispatch part. If it wants to dispatch, the data must be
TodoContent.js
import React,{ Component,Fragment } from 'react' import { connect } from 'react-redux' const Item = (props)=>{ return (<li>{ props.item.text }</li>) } class TodoContent extends Component { renderItem = () => { return this.props.todos.todos.map( (item,index)=>{ return <Item item= { item } key ={ item.id }></Item> }) } render () { return ( <Fragment> <ul> { this.renderItem() } </ul> </Fragment> ) } } // export default TodoContent export default connect( state => state )(TodoContent)
Summary Redux thunk
Its usage is very simple:
- Install Redux thunk
- When creating a store, use the middleware import {createstore, applymeddleware} from 'Redux' import thunk from 'Redux thunk' import reducer from '. / reducer' const store = createstore (reducer, applymeddleware (thunk))
- At this time, the actionCreator method can return a function that can receive the dispatch. After performing asynchronous operations in this function, we can send the action created by actionCreator to the