I Love ReactJS

Redux-getting started instance TodoList

redux-- getting started with instance TodoList

Tip

the front-end technology is really changing with each passing day. I'm embarrassed that I don't have a data stream when I finish React.
looked through flux with anticipation, and was simply impressed by the official stream of consciousness documents. Is it really smelly and long, or is it my IQ problem? 😖
turned to redux. The more you read it, the more interesting it becomes. Follow the document to make a small example of how to get started with TodoList.

Don't say much nonsense, first post the source code github.com/TongchengQi of the examples used in the article.
Github warehouse github.com/rackt/redux of redux

advantage

with the development of spa (not SPA, but single-page applications), in the case of react, the idea of componentization and state machines really liberates the vexed dom operation, and everything is in a state. State to manipulate changes in views.
however, because of the componentization of the page, each component must maintain its own set of states, which is fine for small applications.
but for larger applications, too many states appear complex, and in the end, it is difficult to maintain, and it is difficult to organize all states clearly, and this is also true in multi-person development, resulting in some unknown changes, and the more troublesome it is to debug later. In many cases, the change of state is out of control. For inter-component traffic, server rendering, routing jump, update debugging, we need a mechanism to clearly organize the state of the entire application, redux should come into being, this idea of data flow is really amazing.

structure of state root object

in react, we try to put the state on the top-level component, using redux or router on the top-level component.
this divides components into two categories: container components and presentation components.
Container components: interact with redux and router, maintain a set of states and trigger action.
presentation component: the presentation component is inside the container component. They do not maintain the state. All data is sent to them through props, and all operations are done through callbacks.
in this way, the architecture of our whole application is clear.

part

redux is divided into three parts, store, action, and reducer.

store

the state of the entire application is stored in an object tree, and this object tree exists only in the only store.
or so, store's accusations are as follows:

to put it this way, the state of the entire application is stored in store, and the container component can get the desired state from store.
the container component can also send an action to store to tell him to change the value of a certain state, so as long as the container component sends an instruction, it can ask store to setState, and then the state of store changes, and the state obtained by the container component changes, resulting in the update of views.

action

action can be understood as an instruction. The only reason for store data is that action,action is an object, which requires at least one element. Type,type is the unique identity of this instruction, and the other elements are the state value of the instruction.

{
  type: ACTION_TYPE,
  text: “content”,
}

this instruction is triggered by the component and then passed to reducer.

reducer

action simply states what to do and the parameter values needed to do it.
to specifically change the state in store is done by reducer.
reducer is actually a function that contains switch. This is not to say that the action triggered by the component will be passed to reducer,reducer to receive this parameter action. He then does different operations through switch (action.type). As mentioned earlier, this type is the ID of the instruction, and reducer makes different operations according to this identity.
what is this operation?
reducer also receives another parameter, state, which is the old state. The parameter values needed to do this operation can also be obtained from action.
this operation actually operates on the original state and the value obtained from action (combining, deleting,...) and then returns a new state to store.

data flow

organize the previous language, the data flow of the whole operation actually looks like this:

  1. store passes the state,getState (), dispatch (), subscribe () of the entire application to the top-level container component;

  2. Container component interacts with three parts:

    • Internal presentation component: the container distributes the state to each component and dispatch (a function that manipulates data) to each component in the form of callback;

    • action: container gets action;

    • reducer: the container can call dispatch (action), which, as mentioned above, will be called to the following subcomponents in the form of a callback, so that different dispatch (action) can be called according to different user actions. After executing this function, the action is passed to reducer, and then see reducer;

    • .

  3. After

  4. reducer gets the action from the container component, he performs different operations according to the parameter action.type. He will also receive the original state in the store, then manipulate the original state and other parameters in the action object, and then return a new object.

  5. reducer return a new object to store,store to update the application status based on the new object.

-A loop ♻️

connect

there is no relationship between Redux and React, they complement and depend on each other, but Redux and React match perfectly.
We can bind them together through the library react-redux

npm install --save react-redux

react-redux provides two things: Provider and connect.

Provider

this Provider is actually a middleware. It wraps another layer on top of the original App Container. Its function is to receive the store in the store as the props, and put the store in the context for the use of the following connect.

connect

this component is the real connection between Redux and React. It is packaged in the outer layer of our container component. It receives the state and dispatch in the store provided by Provider above, passes it to a constructor, returns an object, and beds it to our container component in the form of attributes.

actual combat TodoList

directory structure

.
├── app                 #Development Catalog
|   |   
|   ├──actions          #Action files
|   |   
|   ├──components       #Internal components
|   |   
|   ├──containers       #Container components
|   |   
|   ├──reducers         #Reducer file
|   |   
|   ├──stores           #Store configuration file
|   |
|   └──index.js         #Entry file
|      
├── dist                #Publish Directory
├── node_modules        #Package folder
├── .gitignore     
├── .jshintrc      
├── server.js           #Local static server      
├── webpack.config.js   #Webpack configuration file
└── package.json

here, we only focus on our app development directory.

index.js entry file

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './stores/configureStore';
const store = configureStore();
render(
  
    
  ,
  document.getElementById('root')
);

here we get a Provider component from react-redux, and we render it to the outermost layer of the application.
he needs an attribute store, and he puts this store in context for App (connect) to use.

store

app/stores.configureStore.js

import { createStore } from 'redux';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
  const store = createStore(rootReducer, initialState);
  if (module.hot) {
    module.hot.accept('../reducers', () => {
      const nextReducer = require('../reducers');
      store.replaceReducer(nextReducer);
    });
  }
  return store;
}

he gets the createStore function from redux, and then the rootReducer; the
createStore function takes two parameters, (reducer, [initialState]), reducer no doubt, he needs to get the state from store and connect to the reducer interaction.
initialState is an optional initialization state that can be customized.
module.hot this can be ignored. This is the hot loading of webpack, or you can leave it alone.

Container components

containers/App.jsx

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import {
  addTodo,
  completeTodo,
  setVisibilityFilter,
  VisibilityFilters
} from '../actions';

import AddTodo from '../components/AddTodo';
import TodoList from '../components/TodoList';
import Footer from '../components/Footer';

class App extends Component {
  render() {
    const { dispatch, visibleTodos, visibilityFilter } = this.props;
    return (
      

        
            dispatch(addTodo(text))
          }
        />
         dispatch(completeTodo(index))}
        />
         dispatch(setVisibilityFilter(nextFilter))}
        />
      

    );
  }
}

App.propTypes = {
  visibleTodos: PropTypes.arrayOf(PropTypes.shape({
    text: PropTypes.string.isRequired,
    completed: PropTypes.bool.isRequired
  })),
  visibilityFilter: PropTypes.oneOf([
    'SHOW_ALL',
    'SHOW_COMPLETED',
    'SHOW_ACTIVE'
  ]).isRequired
};


function selectTodos(todos, filter) {
  switch (filter) {
    case VisibilityFilters.SHOW_ALL:
      return todos;
    case VisibilityFilters.SHOW_COMPLETED:
      return todos.filter(todo => todo.completed);
    case VisibilityFilters.SHOW_ACTIVE:
      return todos.filter(todo => !todo.completed);
  }
}

// The state here is the component of Connect
function select(state) {
  return {
    visibleTodos: selectTodos(state.todos, state.visibilityFilter),
    visibilityFilter: state.visibilityFilter
  };
}

export default connect(select)(App);

he gets the connect connection component from react-redux and connects the store and App container components through connect (select) (App) .
select is a function that receives a state parameter, which is the state in store, and then returns an object through the processing of this function, passing the parameters in the object as properties to App, along with a dispatch.
so in App, you can:

const { dispatch, visibleTodos, visibilityFilter } = this.props;

so App passes state to child components through connect to state and dispatch. The
dispatch function takes an action parameter and then performs the operation in reducer.
for example:

text =>
  dispatch(addTodo(text))

addTodo (text) . This function is obtained in action. You can see the code of action. It actually returns an action object, so it is actually dispatch (action) .

action

app/actions/index.js

export const ADD_TODO = 'ADD_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO';
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE',
};

export function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  };
}

export function completeTodo(index) {
  return {
    type: COMPLETE_TODO,
    index
  };
}

export function setVisibilityFilter(filter) {
  return {
    type: SET_VISIBILITY_FILTER,
    filter
  };
}

when declaring each return action function, we need to declare the type of the action in the header so that it can be well organized and managed.
each function returns an action object, so call

in the container component.

text =>
  dispatch(addTodo(text))

is to call dispatch (action) .

reducer

app/reducers/visibilityFilter.js

import {
  SET_VISIBILITY_FILTER,
  VisibilityFilters
} from '../actions';

const { SHOW_ALL } = VisibilityFilters;

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter;
    default:
      return state;
  }
}

export default visibilityFilter;

here we get the parameters of each type from actions to map to action.
the whole function actually executes switch, returning different object states according to different action.type.
but if we need a lot of type, such as todos in addition to visibilityFilter, should we write a long switch, of course not.
redux provides a combineReducers helper function that combines an object of several different reducer functions as value into a final reducer function, and then you can call createStore on this reducer.
We put different reducer under different files.
app/reducers/todo.js

import {
  ADD_TODO,
  COMPLETE_TODO
} from '../actions';

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ];
    case COMPLETE_TODO:
      return [
        ...state.slice(0, action.index),
        Object.assign({}, state[action.index], {
          completed: true
        }),
        ...state.slice(action.index + 1)
      ];
    default:
      return state
  }
}

export default todos;

then merge them through an index.js.
app/reducers/index.js

import { combineReducers } from 'redux';
import todos from './todos';
import visibilityFilter from './visibilityFilter';

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

export default rootReducer;

display components

app/components/AddTodo/index.jsx

import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';

export default class AddTodo extends Component {
  render() {
    return (
      

        
         this.handleClick(e) }>
          Add
        
      

    );
  }

  handleClick(e) {
    const inputNode = findDOMNode(this.refs.input);
    const text = inputNode.value.trim();
    this.props.onAddClick(text);
    inputNode.value = '';
  }
}

AddTodo.propTypes = {
  onAddClick: PropTypes.func.isRequired
};

app/components/Todo/index.jsx

import React, { Component, PropTypes } from 'react';

export default class Todo extends Component {
  render() {
    const { onClick, completed, text } = this.props;
    return (
      
        {text}
      
    );
  }
}

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  text: PropTypes.string.isRequired,
  completed: PropTypes.bool.isRequired
};

app/components/TodoList/index.jsx

import React, { Component, PropTypes } from 'react';
import Todo from '../Todo';

export default class TodoList extends Component {
  render() {
    return (
      

        {
          this.props.todos.map((todo, index) =>
             this.props.onTodoClick(index)}
              key={index}
            />
          )
        }
      

    );
  }
}

TodoList.propTypes = {
  onTodoClick: PropTypes.func.isRequired,
  todos: PropTypes.arrayOf(PropTypes.shape({
    text: PropTypes.string.isRequired,
    completed: PropTypes.bool.isRequired
  }).isRequired).isRequired
};

app/components/Footer/index.jsx

import React, { Component, PropTypes } from 'react';

export default class Footer extends Component {
  renderFilter(filter, name) {
    if(filter == this.props.filter) {
      return name;
    }
    return (
       {
          e.preventDefault();
          this.props.onFilterChange(filter);
        }}>
        {name}
      
    );
  }

  render() {
    return (
      


        SHOW
        {' '}
        {this.renderFilter('SHOW_ALL', 'All')}
        {', '}
        {this.renderFilter('SHOW_COMPLETED', 'Completed')}
        {', '}
        {this.renderFilter('SHOW_ACTIVE', 'Active')}
        .
      


    );
  }
}

Footer.propTypes = {
  onFilterChange: PropTypes.func.isRequired,
  filter: PropTypes.oneOf([
    'SHOW_ALL',
    'SHOW_COMPLETED',
    'SHOW_ACTIVE'
  ]).isRequired
};

as you can see, all the state and data required by the presentation component are obtained from the properties, and all operations are performed through the callback function given by the container component.
they try not to have their own state and do stateless components.

end

with regard to the use of redux, this is only a basic introduction, and there are many basic operations, such as asynchronous data flow, Middleware, and router.

Exit mobile version