Note: this is the article of October 2016, moved from my blog…

Github.com/BuptStEve/b…

Zero, environment building

The resources

  • Original English document
  • Chinese document
  • Wall crack recommended author out of the teaching video foundation
  • Wall crack recommended author out of the teaching video advanced

To be clear, although Redux evolved from Flux, we can and should learn from React without getting bogged down in all the details at first.

Therefore, it is recommended to use jsbin for debugging learning, or create-React-app for project scaffolding.

What is Redux?

Predictable State Container Redux is a Predictable state container for JavaScript apps.

Forget the details for a moment

  • In general, Redux uses store to store and manage various states in a page.
  • When a state change is required, the Action Creators is triggered using a call to Dispatch
  • These actions are then processed using the pure function Reducer, which returns the new state based on the current state and action (note that it is not modified here)
  • The View layer can subscribe to states and get new states to refresh the interface (so it’s perfect for a data-driven front-end framework).

Pure function: simply put, a function that always returns the same output for the same input and has no side effects. (It is recommended to learn functional programming)

1.1. Why redux?

  • With the increasing complexity of JavaScript single-page application development, JavaScript needs to manage more states than ever before. These states may include server responses, cached data, locally generated data that has not yet been persisted to the server, as well as UI states such as active routes, selected labels, whether loading activity or pagers are displayed, and so on.
  • Managing changing states is difficult. If a change in one model causes a change in another model, then a change in the View may cause a change in the corresponding model as well as in the other model, which in turn may cause a change in the other view. Until you can’t figure out what’s going on. When, for what reason, and how state changes are out of control. When systems become complex, it can be difficult to reproduce problems or add new features.
  • If that’s not bad enough, consider some of the new requirements from the front-end development world, such as update tuning, server-side rendering, routing pre-jump request data, and so on. Front-end developers are experiencing unprecedented complexity and just give up? Of course not.
  • Much of the complexity here comes from the fact that we often confuse two difficult concepts: change and asynchrony. I call them Mentos and Coke. If you separate them, you can do well, but when you mix them together, they get messy. Some libraries such as React attempt to address this problem by disabling asynchronous and direct DOM manipulation at the view layer. The fly in the ointment is that React still leaves it up to you to handle the data in state. Redux is there to help you solve that problem.
  • Following in the footsteps of Flux, CQRS, and Event Sourcing, Redux tries to make state changes predictable by limiting when and how updates occur. These constraints are reflected in Redux’s three principles.

The short answer is that with Redux we canNo caries (fog)

  • Predictable app state, so app behavior is predictable
  • Because Reducer is a pure function, it is convenient to automate the test of state migration
  • Easy logging, and even time travel

1.2. The Three Principles (Wisdom and Learning)

1.2.1. Single Source of Truth

The state of the entire application is stored in an object tree that exists in only one store.

  • State from the server can be serialized and injected into the client without writing more code
  • For easy debugging, you can keep the state locally during development
  • Undo/Redo can be easily implemented to enable time travel

1.2.2. State is read-only

The only way to change state is to trigger an action, which is a generic object that describes events that have occurred.

Because all changes are centralized and executed strictly one after the other (dispatch calls reduce functions synchronously), there is no need to worry about race conditions. Actions are just ordinary objects, so they can be logged, serialized, stored, and played back during debugging or testing.

1.2.3. Changes are made with pure functions

To describe how an action changes the State Tree, you need to write a Reducer.

The Reducer is just a pure function that receives the previous state and action and returns the new state. You can start with just a Reducer, and as your application grows, you can break it down into smaller reducers that work independently on different parts of the State tree.

Redux foundation

2.1. The action

An Action is just a plain JavaScript Object.

The only limitation of Redux is that there must be a type attribute to indicate which operation to perform. The value should be a string, not Symbols, because strings can be serialized.

Other properties are used to pass the data needed for this operation, which Redux does not restrict, but can refer to the Flux standard Action at design time.

Flux Standard Action

  1. An action must be a JavaScript Object and have a Type attribute.
  2. An action can have payload/error/meta attributes.
  3. An action cannot have other properties.

2.2. reducer

The job of the Reducer is to receive the old state and action and return the new state.

(previousState, action) => newState

Reducer is called because it will be passed to array.prototype. reduce(Reducer,? The initialValue) method. It is important to keep reducer pure. Never do these things in a Reducer:

  • Modify the passed parameter;
  • Perform operations that have side effects, such as API requests and route jumps;
  • Call impure functions, such as date.now () or math.random ().

2.3. The store

A Store is an object that maintains all of the state tree of an application.

There is only one store in Redux (as opposed to multiple stores in Flux), where all state is stored, and it can be thought of as a class that encapsulates state. There is no way to change the internal state except for an action on its dispatch.

In practice, we just need to pass the reducer function at the root to createStore to get a store.

import { createStore } from 'redux';

function reducer(state, action) {
    switch (action.type) {
        case 'SOME_ACTION':
            // Some operations
            return newState; // Return to the new state
        default:
            returnstate; }}const store = createStore(reducer);
Copy the code

These apis are provided in Redux to operate the Store

2.3.1. getState

Returns the current entire state tree.

2.3.2. Dispatch (action)

Distribute actions to the corresponding Reducer.

This function calls getState() and the incoming action calls the Store reduce function synchronously and returns the new state. The state is updated, and the change Listener is triggered. (Put it in the Action Creator step for asynchronous operations)

2.3.3. Subscribe (the listener)

Add a change listener to the store, which is executed every time at dispatch. You can use getState() in the listener (callback function) to get the current state.

The API is interesting in that it returns a function that you can execute to unsubscribe.

2.3.4. ReplaceReducer (nextReducer)

Replace the reducer that the store is currently using to calculate state.

This is a high-level API. This should only be used if you need to implement code separation and need to load some reducer immediately. It may also be used when implementing the Redux hot loading mechanism.

2.4. createStore

Ignoring the various type judgments, a minimalist createStore can be implemented with the following code. The resources

const createStore = (reducer) = > {
    let state;
    let listeners = [];

    const getState = (a)= > state;

    const dispatch = (action) = > {
        state = reducer(state, action); / / call the reducer
        listeners.forEach(listener= > listener()); // Call all change listeners
    };

    const subscribe = (listener) = > {
        listeners.push(listener);

        return (a)= > {
            // return unlisten function
            listeners = listeners.filter(l= >l ! == listener); }; } dispatch({});/ / initialization

    return { getState, dispatch, subscribe };
};

Copy the code

2.5. Counter examples

  • Pure JavaScript does not touch the interface (you can try store.dispatch in the console on the right) {% iframe jsbin.com/kejezih/edi… 100% 600%}
  • Add interface {% iframe jsbin.com/jihara/edit… 100% 600%}

Combine React with React

3.1. Import react with script tags

A Counter that does the same thing

{% iframe jsbin.com/qalevu/edit… 100% 800%}

Implement TodoApp with Redux and React

Before adding react-redux, let’s implement a TodoApp that is a bit more complex than a counter

3.2.1. Analysis and design

1. Container components V.S. presents components

Components are generally divided into

  • Smart/Container Components
  • Dumb/Presentational Components
Container components Display components
Location At the top level, routing Intermediate and child components
Aware of Redux is no
Read the data Get state from Redux Get data from props
Modify the data Send actions to Redux Call the callback function from props

It is generally a best practice for container components to take care of some data fetching, dispatch, and so on. The presentation component component should not care about logic, all data is passed through props.

In this way, the display components can be reused in multiple places, which is packaged by container components and provided with all kinds of data required.

2. Application design
  • A TodoApp consists of three parts:
    • AddTodo input section at the top
    • The TodoList display in the middle
    • The Footer filter section at the bottom
  • State should contain:
    • Filter: indicates the toDOS filter condition
      • SHOW_ALL
      • SHOW_ACTIVE
      • SHOW_COMPLETED
    • Todos: All todos
      • Todo: contains the ID, text, and completed
  • However, the props passed to the application only needs to:
    • VisibleTodos: Filtered ToDOS
    • Filter: indicates the filter criteria
  • There should be three types of Action:
    • ADD_TODO
    • TOGGLE_TODO
    • SET_VISIBILITY_FILTER

3.2.2. Coding implementation

1. The action section
// Use numbers as ids for the moment
let nextTodoId = 0;

/*-- action creators --*/
const addTodo = (text) = >({type: 'ADD_TODO'.id: nextTodoId++, text }
);

const toggleTodo = (id) = >({type: 'TOGGLE_TODO', id }
);

const setVisibilityFilter = (filter) = >({type: 'SET_VISIBILITY_FILTER', filter }
);
Copy the code
2. The reducer parts
// Default initial state
const initialState = { filter: 'SHOW_ALL'.todos: []};function rootReducer(state = initialState, action) {
    switch (action.type) {
        case 'ADD_TODO':
            // Object destruct
            const { id, text } = action;

            return {
                ...state,
                todos: {
                    ...state.todos,
                    { id, text, completed: false,}}};case 'TOGGLE_TODO':
            return {
                ...state,
                todos: state.todos.map(todo= > {
                    if(todo.id ! == action.id)return todo;

                    return {
                        ...todo,
                        completed:! todo.completed, }; })};case 'SET_VISIBILITY_FILTER':
            return {
                ...state,
                filter: action.filter,
            };

        default:
            returnstate; }}Copy the code

Attention!

  1. Instead of modifying the old state directly, return a new state. You can create a new state using object.assign (). You cannot use object.assign (state, {visibilityFilter: action.filter}) as this will change the value of the first argument. You must set the first parameter to empty object. You can also turn on support for the ES7 proposal object expansion operator to use {… state, … NewState} serves the same purpose.
  2. The old state is returned in the case of default to accommodate errors such as encountering unknown actions.

Splitting the reducer code at present looks rather tedious. In fact, the processing of TODOS and filter should be separated logically. Therefore, when the states are not coupled with each other, they can be split so that the Reducer can deal with the subtrees of the corresponding states in a fine way.

// Process a single todo
const todoReducer = (state, action) = > {
    switch (action.type) {
        case 'ADD_TODO':
            return {
                id: action.id,
                text: action.text,
                completed: false};case 'TOGGLE_TODO':
            if(state.id ! == action.id)return state;

            return {
                ...state,
                completed: !state.completed,
            };

        default:
            returnstate; }};/ / processing todos
const todosReducer = (state = [], action) = > {
    switch (action.type) {
        case 'ADD_TODO':
            return [
                ...state,
                todoReducer(undefined, action),
            ];

        case 'TOGGLE_TODO':
            return state.map(t= > todoReducer(t, action));

        default:
            return state;
    };
};

/ / filter processing
const filterReducer = (state = 'SHOW_ALL', action) = > {
    switch (action.type) {
        case 'SET_VISIBILITY_FILTER':
            return action.filter;

        default:
            return state;
    };
};

const rootReducer = (state = initialState, action) = > ({
    todos: todosReducer(state.todos, action),
    filter: filterReducer(state.filter, action),
});
Copy the code

Note that the last rootReducer function returns a new state that has been reduced and merged with the reducer.

Todos: todos(state.todos, action), passed to state.todos, must return todos(because both are nodes in the state tree).

Therefore, Redux provides a very useful combineReducers API to simplify the merge of reducer.

import { combineReducers } from 'redux';

const rootReducer = combineReducers({
    todos: todosReducer,
    filter: filterReducer,
});

// initialState can be passed in as a second argument
const store = createStore(rootReducer, initialState);
Copy the code

Moreover, if the reducer has the same name as the state node (i.e. TodosReducer -> todos), it can be further simplified by es6 syntax

import { combineReducers } from 'redux';

const rootReducer = combineReducers({ todos, filter });

// initialState can be passed in as a second argument
const store = createStore(rootReducer, initialState);
Copy the code

As the application expands, we can also put the split Reducer into different files to keep it independent and dedicated to different data domains.

3. The view part
1. Only the root component

First, we just write a root component
. The Store passes TodoApp through props and subscribes and unsubscribes at componentDidMount and componentWillUnmount, respectively, during the lifecycle.

import React, { Component } from 'react';

class TodoApp extends Component {
    // Subscription store changes
    componentDidMount() {
        const { store } = this.props;

        this.unsubscribe = store.subscribe(
            this.forceUpdate.bind(this)); }// Unsubscribe
    componentWillUnmount() {
        this.unsubscribe();
    }

    Render a single todo
    _renderTodo(todo) {
        const { store } = this.props;

        return (
            <li
                key={todo.id}
                onClick={()= > store.dispatch(toggleTodo(todo.id))}
                style={{
                    textDecoration: todo.completed
                        ? 'line-through'
                        : 'none',
                    cursor: todo.completed
                        ? 'default'
                        : 'pointer',
                }}
            >
                {todo.text}
            </li>
        );
    }

    // Return a string or a link, depending on whether the current filter matches
    _renderFilter(renderFilter, name) {
        const { store } = this.props;
        const { filter } = store.getState();

        if (renderFilter === filter) return name;

        return (
            <a href=The '#' onClick={e= > {
                e.preventDefault();
                store.dispatch(setVisibilityFilter(renderFilter))
            }}>
                {name}
            </a>
        );
    }

    // Filter todos to be rendered based on the current filter
    _getVisibleTodos(todos, filter) {
        switch (filter) {
            case 'SHOW_ALL':
                return todos;

            case 'SHOW_COMPLETED':
                return todos.filter(todo= > todo.completed);

            case 'SHOW_ACTIVE':
                return todos.filter(todo= >! todo.completed);default:
                return todos;
        }
    }

    render() {
        const { store } = this.props;
        const { todos, filter } = store.getState();

        let input;

        return (
            <div>
                {/* AddTodo */}
                <input type="text" ref={node= > input = node} />
                <button onClick={()= >{ if (! input.value) return; store.dispatch(addTodo(input.value)); input.value = ''; }}> addTodo</button>

                {/* TodoList */}
                <ul>
                    {this._getVisibleTodos(todos, filter)
                        .map(this._renderTodo.bind(this))
                    }
                </ul>

                {/* Footer */}
                <p>
                    Show:
                    {' '}
                    {this._renderFilter('SHOW_ALL', 'all')}
                    {', '}
                    {this._renderFilter('SHOW_COMPLETED', 'completed')}
                    {', '}
                    {this._renderFilter('SHOW_ACTIVE', 'active')}
                </p>
            </div>); }}Copy the code

TodoApp has only the root component {% iframe jsbin.com/bodise/edit… 100% 800%}

2. Split components

It is too bloated to write all the interface content in TodoApp, so it will be divided into sub-components (all presentation components) based on the previous analysis results.

  • AddTodo
  • TodoList
    • Todo
  • Footer
    • FilterLink
const AddTodo = ({ onAddClick }) = > {
    let input;

    return( <div> <input type="text" ref={node => input = node} /> <button onClick={() => { onAddClick(input.value); input.value = ''; }}> addTodo </button> </div> ); }; const Todo = ({ text, onClick, completed }) => ( <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none', cursor: completed ? 'default' : 'pointer', }} > {text} </li> ); const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo => <Todo key={todo.id} {... todo} onClick={() => onTodoClick(todo.id)} /> )} </ul> ); const FilterLink = ({ filter, onClick, renderFilter, children }) => { if (renderFilter === filter) return (<span>{children}</span>); return ( <a href='#' onClick={e => { e.preventDefault(); onClick(renderFilter); }}> {children} </a> ); }; const Footer = ({ filter, onFilterClick }) => ( <p> Show: {' '} <FilterLink filter={filter} renderFilter="SHOW_ALL" onClick={onFilterClick} > all </FilterLink> {', '} <FilterLink filter={filter} renderFilter="SHOW_COMPLETED" onClick={onFilterClick} > completed </FilterLink> {', '} <FilterLink filter={filter} renderFilter="SHOW_ACTIVE" onClick={onFilterClick} > active </FilterLink> </p> );Copy the code

So TodoApp is streamlined like this

class TodoApp extends Component {
    // ...

    render() {
        const { store } = this.props;
        const { todos, filter } = store.getState();

        return (
            <div>
                <AddTodo
                    onAddClick={text => {
                        if (!text) return;

                        store.dispatch(addTodo(text));
                    }}
                />

                <TodoList
                    todos={this._getVisibleTodos(todos, filter)}
                    onTodoClick={id => store.dispatch(toggleTodo(id))}
                />

                <Footer
                    filter={filter}
                    onFilterClick={filter => {
                        store.dispatch(setVisibilityFilter(filter));
                    }}
                />
            </div>
        );
    }
}
Copy the code
3. Add container components

For now, we are still using TodoApp as a container component, where each child component is a presentation component.

But doing so means that whenever a child component needs a property, it needs to be passed down from the root component, such as the Filter property in FilterLink.

So let’s add a container component and let the presentation component get the required properties from the container component.

  • AddTodo(container)
  • VisibleTodoList(container)
    • TodoList
      • Todo
  • Footer
    • FilterLink(container)
      • Link
// Store. Dispatch is put back,
// Because for now we only use the AddTodo action in AddTodo
// After the new form is added, we can consider moving store.dispatch out
const AddTodo = ({ store }) = > {
    let input;

    return( <div> <input type="text" ref={node => input = node} /> <button onClick={() => { if (! input.value) return; store.dispatch(addTodo(input.value)); input.value = ''; }}> addTodo </button> </div> ); }; const Todo = ({ text, onClick, completed }) => ( <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none', cursor: completed ? 'default' : 'pointer', }} > {text} </li> ); const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo => <Todo key={todo.id} {... todo} onClick={() => onTodoClick(todo.id)} /> )} </ul> ); ComponentDidMount () {const {store} = this.props; // componentDidMount() {const {store} = this.props; this.unsubscribe = store.subscribe( this.forceUpdate.bind(this) ); } // Unsubscribe componentWillUnmount() {this.unsubscribe(); } // Todos _getVisibleTodos(todos, filter) {switch (filter) {case 'SHOW_ALL': return todos; case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed); case 'SHOW_ACTIVE': return todos.filter(todo => ! todo.completed); default: return todos; } } render() { const { store } = this.props; const { todos, filter } = store.getState(); return ( <TodoList todos={this._getVisibleTodos(todos, filter)} onTodoClick={id => { store.dispatch(toggleTodo(id)) }} /> ); }} // Remove filter and renderFilter attributes from FilterLink. Const Link = ({active, onClick, children}) => {if (active) return (<span>{children}</span>); return ( <a href='#' onClick={e => { e.preventDefault(); onClick(); }}> {children} </a> ); }; // Class FilterLink extends Component {// Subscribe store changes componentDidMount() {const {store} = this.props; this.unsubscribe = store.subscribe( this.forceUpdate.bind(this) ); } // Unsubscribe componentWillUnmount() {this.unsubscribe(); } render() { const { store, renderFilter, children } = this.props; const { filter } = store.getState(); return ( <Link active={filter === renderFilter} onClick={() => store.dispatch( setVisibilityFilter(renderFilter) )} > {children} </Link> ); }} // Display component const Footer = ({store}) => (<p> Show: {' '} <FilterLink store={store} renderFilter="SHOW_ALL" > all </FilterLink> {', '} <FilterLink store={store} renderFilter="SHOW_COMPLETED" > completed </FilterLink> {', '} <FilterLink store={store} renderFilter="SHOW_ACTIVE" > active </FilterLink> </p> ); // Without using the global store variable, // can only be passed in via props for now, Const TodoApp = ({store}) => (<div> <AddTodo store={store} /> <VisibleTodoList store={store} /> <Footer store={store} /> </div> );Copy the code

Looking at the refactored code reveals three troubling things

  1. The root component needs to pass stores to its children via props
  2. Each container component defines componentDidMount to subscribe to and componentWillUnmount to unsubscribe
  3. The app doesn’t actually need to render all of todos, so internally it’s very cumbersome to define_getVisibleTodosfunction
4. Provider

Let’s take advantage of the context feature provided by React

class Provider extends Component {
    // Use this method to inject store into the children context
    getChildContext() {
        return { store: this.props.store };
    }

    render() {
        return this.props.children; }}// You must declare the type of store passed to the context
Provider.childContextTypes = {
    store: React.PropTypes.object,
};
Copy the code

How do you use TodoApp from the top down

// 1. Use Provider to wrap TodoApp and pass store as props
ReactDOM.render(
    <Provider store={createStore(rootReducer, initialState)} >
        <TodoApp />
    </Provider>.document.getElementById('container'));// 2. Root TodoApp: and store say goodbye~,
// TodoApp is not a container component
const TodoApp = (a)= > (
    <div>
        <AddTodo />
        <VisibleTodoList />
        <Footer />
    </div>
);

// 3. AddTodo: because props is fixed as the first argument passed to the child component,
// so {store} needs to be declared in the second place, but it needs to declare contextTypes...
const AddTodo = (props, { store }) = > {
    // ...
};
// Must be declared
AddTodo.contextTypes = {
    store: React.PropTypes.object,
};

// 4. VisibleTodoList: < props >
// Also declare contextTypes...
class VisibleTodoList extends Component {
    // Subscription store changes
    componentDidMount() {
        const { store } = this.context; // props -> context

        // ...
    }

    // ...

    render() {
        const { store } = this.context; // props -> context
        const { todos, filter } = store.getState();

        // ...}}// Must be declared
VisibleTodoList.contextTypes = {
    store: React.PropTypes.object,
};

// TodoList and Todo remain the same

// 5. Footer: and store say goodbye...
const Footer = (a)= > (
    <p>
        Show:
        {' '}
        <FilterLink renderFilter="SHOW_ALL">
            all
        </FilterLink>
        {', '}
        <FilterLink renderFilter="SHOW_COMPLETED">
            completed
        </FilterLink>
        {', '}
        <FilterLink renderFilter="SHOW_ACTIVE">
            active
        </FilterLink>
    </p>
);

// 6. FilterLink: same as VisibleTodoList (props + contextTypes...)
class FilterLink extends Component {
    // Subscription store changes
    componentDidMount() {
        const { store } = this.context; // props -> context

        // ...
    }

    // ...

    render() {
        const { renderFilter, children } = this.props;
        const { store } = this.context; // props -> context
        const { filter } = store.getState();

        // ...}}// Must be declared
FilterLink.contextTypes = {
    store: React.PropTypes.object,
};

// -- Link unchanged --
Copy the code

Now the non-container components in the middle don’t have to bother passing store={store} for their children at all, so above we implement a simplified version of the first component provided by React-Redux.

ContextTypes are annoying to keep writing contextTypes, and context is not stable, so context should not be written directly in our application code.

Will the meter be installed?

5. connect
  • OOP thinking: Isn’t that easy? ComponentDidMount, componentWillUnmount, and contextTypes subclasses (componentDidMount, componentWillUnmount, contextTypes

Congratulations ~ object – oriented thinking is very good ~

Although everything at the bottom of JavaScript is object oriented, but in the front once associated with the interface, it will be very troublesome to copy the object oriented method implementation…

  • React early users: Isn’t that easy? Isn’t it beautiful to write a mixin?

React’s own mixins do provide some convenience in sharing methods between components, but components that use mixins need to know the details to avoid state contamination, so they become increasingly difficult to maintain once the number of mixins increases.

Unfortunately, we will not launch any mixin support for ES6 classes in React. That would defeat the purpose of only using idiomatic JavaScript concepts.

Support for mixins in ES6 classes has been officially dropped.

  • Functional (FP) : The High Order Component (hoc) is the ultimate solution

hocFactory:: W: React.Component => E: React.Component

As shown above, hoc’s constructor receives a W (for WrappedComponent) and returns an E (for Enhanced Component), which is the higher-order Component.

Suppose we have an old component Comp, but the goose now receives some changes in parameters.

Of course you can copy and paste and modify the code of the old component… (Hero is worshipped by the nest)

Alternatively, return a new component to wrap the old component.

class NewComp extends Component {
    mapProps(props) {
        return {/* new props */};
    }

    render() {
        return (<Comp {. this.mapProps(this.props)} / >); }}Copy the code

But what if there are more components of the same logic that need to be adapted? Can’t have several copy several times…

So have you heard of advanced components?

// Returns a function that returns a new component
const mapProps = mapFn= > Comp => {
    return class extends Component {
        render() {
            return (<Comp {. this.mapProps(this.props)} / >); }}; }; const NewComp = mapProps(mapFn)(Comp); // Note that it was called twiceCopy the code

As you can see, we decoupled mapFn from Comp with the help of higher-order components, so that no matter how much more nested change logic is needed, it is not a problem

React-redux provides the second and final API — connect returns a higher-order component.

Just connect()(WrappedComponent) returns the Component that automatically completes the subscription store in componentDidMount, Unsubscribe and declare contextTypes in componentWillUnmount.

That leaves just one more problem

3. The app doesn’t actually need to render all of todos, so the _getVisibleTodos function is intricatelydefined

The first parameter of the connect function is called mapStateToProps. It is used to pass the data in the store as props, so that the internal component can efficiently call it directly. That solves the last problem

Goose, we ask ourselves, is that enough? And no…

One last detail, FilterLink for example.

class FilterLink extends Component {
    // ...

    render() {
        const { store, renderFilter, children } = this.props;
        const { filter } = store.getState();

        return (
            <Link
                active={filter= = =renderFilter}
                onClick={()= > store.dispatch(
                    setVisibilityFilter(renderFilter)
                )}
            >
                {children}
            </Link>); }}Copy the code

In addition to getting the data (filter) from the Store, we also get the Dispatch from it to trigger the action. If we added the content of the onClick callback to props, wouldn’t the entire logic of FilterLink be abstracted with connect?

The second parameter to connect is called mapDispatchToProps, which abstracts each call to the Dispatch as a function added to the props and passed to the internal component. Finally, the last problem was solved

const mapStateToLinkProps = (state, ownProps) = > ({
    // ownProps is the props of the original component,
    // This is to distinguish props from higher-order components
    active: ownProps.renderFilter === state.filter,
});

const mapDispatchToLinkProps = (dispatch, ownProps) = > ({
    onClick: (a)= >{ dispatch( setVisibilityFilter(ownProps.renderFilter) ); }});// Note that the entire FilterLink has been deleted
const FilterLink = connect(
    mapStateToLinkProps,
    mapDispatchToLinkProps
)(Link);
Copy the code

TodoApp uses react-redux {% iframe jsbin.com/fumihi/edit… 100% 800%}