Redux introductory tutorial (3): the usage of React-Redux
The first two tutorials introduced the basic usage and asynchronous operations of Redux, and today is the last part, which introduces how to use Redux in a React project.
For ease of use, the author of Redux encapsulated a React-specific library React-Redux , this article mainly introduces it.
This library is optional. In actual projects, you should weigh whether to use Redux directly or React-Redux. Although the latter provides convenience, it needs to master additional APIs and comply with its component splitting specifications.
One, UI components
React-Redux divides all components into two categories: UI components (presentational components) and container components (container components).
UI components have the following characteristics.
- Only responsible for the presentation of the UI, without any business logic
- No state (that is,
this.state
this variable is not used )- All data are
this.props
provided by parameters ( )- Do not use any Redux API
The following is an example of a UI component.
const Title = value => <h1>{value}</h1>;
Because it does not contain state, UI components are also called “pure components”, that is, they are pure functions, and their values are determined purely by parameters.
Two, container components
The characteristics of container components are just the opposite.
- Responsible for managing data and business logic, not for UI presentation
- With internal state
- Use Redux API
In short, just remember one sentence: UI components are responsible for the presentation of the UI, and container components are responsible for managing data and logic.
You may ask, what if a component has both UI and business logic? The answer is, split it into the following structure: the outside is a container component, and the inside is a UI component. The former is responsible for communicating with the outside, passing data to the latter, and the latter renders the view.
React-Redux stipulates that all UI components are provided by users, and container components are automatically generated by React-Redux. In other words, the user is responsible for the visual layer, and state management is all left to it.
Three, connect()
React-Redux provides
connect
methods for generating container components from UI components.
connect
The meaning is to connect these two components.
import { connect } from 'react-redux' const VisibleTodoList = connect()(TodoList);
In the above code, it
TodoList
is a UI component, which
VisibleTodoList
is
connect
a container component that is automatically generated
by React-Redux through
methods.
However, because there is no business logic defined, the above container component is meaningless, just a pure packaging layer for UI components. In order to define the business logic, the following two aspects of information need to be given.
(1) Input logic:
state
How to convert external data (ie objects) into parameters of UI components(2) Output logic: how the action sent by the user becomes an Action object and is transmitted from the UI component.
Therefore,
connect
the complete API of the method is as follows.
import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList)
In the above code, the
connect
method accepts two parameters:
mapStateToProps
and
mapDispatchToProps
.
They define the business logic of UI components.
The former is responsible for the input logic, which is about to
state
map to the parameters (
props
) of the
UI component
, and the latter is responsible for the output logic, which is to map the user’s operations on the UI component into Action.
4, mapStateToProps ()
mapStateToProps
Is a function.
Its role is to establish a
mapping relationship
from (external)
state
objects to (UI component)
props
objects,
just like its name
.
As a function,
mapStateToProps
it should return an object after execution, and each key-value pair in it is a map.
Please see the example below.
const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } }
In the above code, it
mapStateToProps
is a function that accepts
state
as a parameter and returns an object.
This object has a
todos
property with the same name parameter represents a UI component, the latter
getVisibleTodos
is a function, from the
state
calculated
todos
values.
The following is
getVisibleTodos
an example to calculate
todos
.
const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) default: throw new Error('Unknown filter: ' + filter) } }
mapStateToProps
It will subscribe to the Store,
state
and it will be automatically executed
whenever it is
updated, and the parameters of the UI components will be recalculated, thereby triggering the re-rendering of the UI components.
mapStateToProps
The first parameter is always an
state
object, and the second parameter can also be used to represent the
props
object of the
container component
.
// 容器组件的代码 // <FilterLink filter="SHOW_ALL"> // All // </FilterLink> const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter === state.visibilityFilter } }
After being used
ownProps
as a parameter, if the parameter of the container component changes, it will also cause the UI component to re-render.
connect
The method can omit
mapStateToProps
parameters. In that case, the UI component will not subscribe to the Store, which means that the update of the Store will not cause the update of the UI component.
五、mapDispatchToProps()
mapDispatchToProps
It is
connect
the second parameter
of the
function, used to establish
store.dispatch
the mapping of
UI component parameters to
methods.
In other words, it defines which user actions should be treated as Actions and passed to the Store.
It can be a function or an object.
If it
mapDispatchToProps
is a function, it will get
two parameters
dispatch
and
ownProps
(the
props
object of the
container component
).
const mapDispatchToProps = ( dispatch, ownProps ) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }); } }; }
As can be seen from the above code,
mapDispatchToProps
as a function, an object should be returned. Each key-value pair of the object is a mapping that defines how the parameters of the UI component emit Actions.
If it
mapDispatchToProps
is an object, each key name of it is also a parameter of the same name corresponding to the UI component. The key value should be a function, which will be treated as an Action creator, and the returned Action will be automatically issued by Redux.
For example, the above is
mapDispatchToProps
written as the following object.
const mapDispatchToProps = { onClick: (filter) => { type: 'SET_VISIBILITY_FILTER', filter: filter }; }
Six, <Provider> component
connect
After the method generates the container component, the container component needs to get the
state
object to generate the parameters of the UI component.
One solution is to pass the
state
object as a parameter to the container component.
However, it is more troublesome to do so, especially the container components may be at a very deep level, and
state
it
will
be troublesome
to
pass on
one level to another
.
React-Redux provides
Provider
components that can be obtained by container components
state
.
import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App' let store = createStore(todoApp); render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
In the above code,
Provider
there is a layer outside the root component, so that
App
all sub-components are available
state
by
default
.
Its principle is
React
the
context
attribute
of the
component
, please see the source code.
class Provider extends Component { getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object }
In the above code, it is
store
placed
context
above
the context object
.
Then, the subassembly can
context
get
store
, the code is as follows.
class VisibleTodoList extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); // ... } } VisibleTodoList.contextTypes = { store: React.PropTypes.object }
React-Redux
The code of the automatically generated container component is similar to the above, so you can get it
store
.
Seven, example: counter
Let’s look at an example. Below is a counter component, which is a pure UI component.
class Counter extends Component { render() { const { value, onIncreaseClick } = this.props return ( <div> <span>{value}</span> <button onClick={onIncreaseClick}>Increase</button> </div> ) } }
In the above code, this UI component has two parameters:
value
and
onIncreaseClick
.
The former needs to
state
be obtained
from
calculations, and the latter needs to send out Actions.
Next, define
value
the
state
mapping as well as
onIncreaseClick
to
dispatch
the mapping.
function mapStateToProps(state) { return { value: state.count } } function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) } } // Action Creator const increaseAction = { type: 'increase' }
Then, use the
connect
method to generate the container component.
const App = connect( mapStateToProps, mapDispatchToProps )(Counter)
Then, define the Reducer of this component.
// Reducer function counter(state = { count: 0 }, action) { const count = state.count switch (action.type) { case 'increase': return { count: count + 1 } default: return state } }
Finally, the
store
object is
generated
and used
Provider
to wrap a layer outside the root component.
import { loadState, saveState } from './localStorage'; const persistedState = loadState(); const store = createStore( todoApp, persistedState ); store.subscribe(throttle(() => { saveState({ todos: store.getState().todos, }) }, 1000)) ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
See the complete code here .
Eight, React-Router routing library
Use
React-Router
project, no different from the other projects, but also the use of
Provider
the
Router
outside layer of the package, after all,
Provider
the only function is to pass
store
objects.
const Root = ({ store }) => ( <Provider store={store}> <Router> <Route path="/" component={App} /> </Router> </Provider> );
(over)