@(blog)
Redux basic tutorial
The readers of this article are those who understand the concept of flux, are familiar with react, and understand ES6 grammar
redux
It’s a hot one recentlyflux
Framework. In just one month, there are now 2900+ stars. After watch, hundreds of PR emails are received every day. There is no more nonsense.
Whyredux
, see connectionThe Evolution of Flux Frameworks。
main features
- Everything (Stores, Action Creators, configuration) is hot reloadable. —— It should be very cool to cooperate with hotloader, although it is used now
browserify
The benefits are not obvious. -
store
The data in is unlimited and can benumber
object
array
Wait, crap, because it’sstore
Just a simple function. - provide
devtools
, monitoringaction
Triggering of andstate
Changes in. - The source code is clear, simple and lightweight. There is no need for documentation. Just look at the source code directly The disadvantage is that if you don’t understand the source code, you won’t feel clear enough just looking at the documents.
-
api
It’s very simple. You don’t have to remember a lotapi
- every thing is simple function
-
connecter
Andprovider
These two things are always cumbersome and not so elegant.
The following is an example of a simple counterredux
The core method and some points needing attention.
- Synchronous and asynchronous actioncreators
- Use of Middleware
- dispatch actions
- Get and synchronize state
Code placed inhttps://github.com/yeatszhang/redux-tutorial, gulp needs to be installed
Code is branch basedv1.0.0-rc
API is slightly different. SeeBreaking API changes for 1.0。
actionCreator
Actions creator is a function used to generate actions. By default, it can accept returnsobject
perhapsfunction
When learning flux, many people confuse action with actioncreator
//Return object directly
actionCreators.addTodo = function(text) {
return {
type: types.ADD_TODO,
text
};
}
//Return function
actionCreators.incrementAsync = function() {
return (dispatch, getState) => {
//In actioncreator, you can get the current state through getstate
console.log(getState());
//Asynchronous action
setTimeout(() => {
dispatch({
type: INCREMENT_COUNTER2,
});
}, 1000);
};
};
Without using anymiddleware
In this case, there are only twoaction
Can bedispatch
。
app
In the outermost layer of dynamic content, the provider should be used for wrapping. The provider receives the store as a parameter. Note that children is afunction
Not at allreactElement
。provider
Pass the store to the child node as a context, and implementstore
Hot swap for. Therefore, components in the provider can actually get the dispatch and state through the context instead of connect. However, the author does not recommend this.
import React from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
// redux midlleware repositories
import thunk from 'redux-thunk';
//Extract the Redux and react related parts, such as the connector provider, separately
import { Provider } from 'react-redux';
import reducers from '../reducers';
import CounterApp from './CounterApp.js';
import logMiddleware from '../middleWares/logMiddleware.js';
const reducer = combineReducers(reducers);
const createStoreWithMiddleware = applyMiddleware(thunk, logMiddleware)(createStore);
const store = createStoreWithMiddleware(reducer);
//Using middleware thunk, you can write directly if there is no requirement to customize the m-server
// const store = createStore(reducer);
class App extends React.Component {
render() {
return (
<Provider store={store}>
{() => <CounterApp />}
</Provider>
);
}
}
smart component
Smart component has two characteristics:
- Automatically associate the states in the store and re render automatically
- Events can be distributed through dispatch to trigger the update of the store
Students who have just come into contact with Redux will certainly find this connect very difficult to understand. Or say in the code…
/**
* Created by yichizhang on 15/7/26.
*/
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { Connector } from 'react-redux';
import Counter from '../components/Counter';
import actionCreators1 from '../actionCreators/actionCreators1.js';
import actionCreators2 from '../actionCreators/actionCreators2.js';
//State is the set of states in each reducer
function select(state) {
//Select the state that the component needs to listen to from each reducer
return {
counter1: state.reducer1.counter,
counter2: state.reducer2.counter,
};
}
export default class CounterApp extends Component {
//The return value of the select function and an object of the dispatch assembly process are used as parameters
//From here, we can see that the connector is to help get the dispatch method of the store in the provider and select the state to be used
renderChild({ counter1, counter2, dispatch}) {
//Personally, I find it inconvenient to use actions in this way, especially when the component only needs to trigger actions and does not need to listen to changes in the store. I will be lazy to get the dispatch through the context~~
const actions1 = bindActionCreators(actionCreators1, dispatch);
const actions2 = bindActionCreators(actionCreators2, dispatch);
const props = { ...actions1, ...actions2, counter1, counter2 };
//All actions and states will be provided to the counter in the form of props, and then you can do whatever you want in the counter~
return <Counter {...props} />;
}
render() {
return (
<Connector select={select}>
{this.renderChild}
</Connector>
);
}
}
reducer
Redux thinks that programmers do not need to write the logic in the store, but only need to write the processing logic for the state:
old sate => action => new state
This is a fully synchronized process. The reducer only needs to declare the initial state and the change rules of the state after receiving the action.
import React from 'react/addons';
import {INCREMENT_COUNTER1, DECREMENT_COUNTER1} from '../constants/actionsTypes.js';
const update = React.addons.update;
//State can be any type
const initialState = {
counter: 0,
};
//Reducer is just a simple switch method
export default function counter(state = initialState, action = null) {
switch (action.type) {
case INCREMENT_COUNTER1:
//It should be noted that the connector will perform a "share equal" operation when the state in the select changes,
//Therefore, if you need to operate the reference value, you must not directly assign a value. You need to use update or immutable in addon JS, I know that I don't want to continue learning these two tools It's simple
//This can greatly avoid repeated render and improve performance
return update(state, {
counter: {
$set: state.counter + 1,
},
});
case DECREMENT_COUNTER1:
return update(state, {
counter: {
$set: state.counter - 1,
},
});
default:
return state;
}
}
middleWare
Interested students can have a look. Generally speaking, the default thunk is enough. I added an intermediate layer of log to the example
//Print triggered action
function logMiddleware() {
//Next here is the next Middleware
return function(next) {
return function(action) {
//Print this action and process it with the next Middleware
console.log(action);
next(action);
};
};
}
export default logMiddleware;
//The following is the default thunk Middleware
function thunkMiddleware(_ref) {
var dispatch = _ref.dispatch;
var getState = _ref.getState;
return function (next) {
return function (action) {
//If it is a function, execute the function with dispatch and getstate as parameters. Otherwise, write a middleware for processing
return typeof action === 'function' ? action(dispatch, getState) : next(action);
};
};
}
epilogue
In fact, where Redux doesn’t understand, it’s better to directly look at the source code. The amount of code in Redux is very small and the organization is very clear. It is recommended that everyone should look at it. However, the author seems to have a heavy mind on functional programming, using a lot of modifier syntax, and reducing~ is very convoluted~
After that, I will summarize my experience in reading the Redux source code and the implementation principle of each functional module~