zoukankan      html  css  js  c++  java
  • [Redux] Extracting Container Components -- Complete

    Clean TodoApp Component, it doesn't need to receive any props from the top level component:

    const TodoApp = () => (
      <div>
        <AddTodo />
        <VisibleTodoList />
        <Footer />
      </div>
    );

    Also we don't need wrap ReactDOM.render() into a render function and subscribe it, because the container component will update itself when state changes:

    // From
    const render = () => {
      ReactDOM.render(
        <TodoApp
          {...store.getState()}
        />,
        document.getElementById('root')
      );
    };
    
    store.subscribe(render);
    render();
    
    // To
    ReactDOM.render(
      <TodoApp
        {...store.getState()}
      />,
      document.getElementById('root')
    );

    The first component I'm looking at is called AddToDo. Frankly, I cant classify it either as a presentational component or as a container component because it doesn't fit either category. The input and the button are the presentational part, but dispatching an action onClick is the behavior which is usually specified by the container.

    However, in this case, I'd rather keep them together because there isn't any state, the UI is very simple. It's hard to imagine any other behavior other than dispatching the AddToDo action.

    -----

    In general, I suggest first trying to extract the presentational components. If there is too much boilerplate passing the props through them, then you can create the containers around them that load the data and specify the behavior.

    --------------

    Code:

    const todo = (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:
          return state;
      }
    };
    
    const todos = (state = [], action) => {
      switch (action.type) {
        case 'ADD_TODO':
          return [
            ...state,
            todo(undefined, action)
          ];
        case 'TOGGLE_TODO':
          return state.map(t =>
            todo(t, action)
          );
        default:
          return state;
      }
    };
    
    const visibilityFilter = (
      state = 'SHOW_ALL',
      action
    ) => {
      switch (action.type) {
        case 'SET_VISIBILITY_FILTER':
          return action.filter;
        default:
          return state;
      }
    };
    
    const { combineReducers } = Redux;
    const todoApp = combineReducers({
      todos,
      visibilityFilter
    });
    
    const { createStore } = Redux;
    const store = createStore(todoApp);
    
    const { Component } = React;
    
    const Link = ({
      active,
      children,
      onClick
    }) => {
      if (active) {
        return <span>{children}</span>;
      }
    
      return (
        <a href='#'
           onClick={e => {
             e.preventDefault();
             onClick();
           }}
        >
          {children}
        </a>
      );
    };
    
    class FilterLink extends Component {
      componentDidMount() {
        this.unsubscribe = store.subscribe(() =>
          this.forceUpdate()
        );
      }
      
      componentWillUnmount() {
        this.unsubscribe();
      }
      
      render() {
        const props = this.props;
        const state = store.getState();
        
        return (
          <Link
            active={
              props.filter ===
              state.visibilityFilter
            }
            onClick={() =>
              store.dispatch({
                type: 'SET_VISIBILITY_FILTER',
                filter: props.filter
              })
            }
          >
            {props.children}
          </Link>
        );
      }
    }
    
    const Footer = () => (
      <p>
        Show:
        {' '}
        <FilterLink
          filter='SHOW_ALL'
        >
          All
        </FilterLink>
        {', '}
        <FilterLink
          filter='SHOW_ACTIVE'
        >
          Active
        </FilterLink>
        {', '}
        <FilterLink
          filter='SHOW_COMPLETED'
        >
          Completed
        </FilterLink>
      </p>
    );
    
    const Todo = ({
      onClick,
      completed,
      text
    }) => (
      <li
        onClick={onClick}
        style={{
          textDecoration:
            completed ?
              'line-through' :
              'none'
        }}
      >
        {text}
      </li>
    );
    
    const TodoList = ({
      todos,
      onTodoClick
    }) => (
      <ul>
        {todos.map(todo =>
          <Todo
            key={todo.id}
            {...todo}
            onClick={() => onTodoClick(todo.id)}
          />
        )}
      </ul>
    );
    
    const AddTodo = ({
      onAddClick
    }) => {
      let input;
    
      return (
        <div>
          <input ref={node => {
            input = node;
          }} />
          <button onClick={() => {
            store.dispatch({
              type: 'ADD_TODO',
              id: nextTodoId++,
              text: input.value
            });
            input.value = '';
          }}>
            Add Todo
          </button>
        </div>
      );
    };
    
    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
          );
      }
    }
    
    class VisibleTodoList extends Component {
      
      componentDidMount() {
        this.unsubscribe = store.subscribe(() =>
          this.forceUpdate()
        );
      }
      
      componentWillUnmount() {
        this.unsubscribe();
      }
      
      render() {
        
        const props = this.props;
        const state = store.getState();
        
        return (
        <TodoList
          todos={
            getVisibleTodos(
              state.todos,
              state.visibilityFilter
            )
          }
          onTodoClick={id =>
            store.dispatch({
              type: 'TOGGLE_TODO',
              id
            })
          }
        />
        );
      }
    }
    
    let nextTodoId = 0;
    const TodoApp = () => (
      <div>
        <AddTodo />
        <VisibleTodoList />
        <Footer />
      </div>
    );
    
    
    ReactDOM.render(
      <TodoApp
        {...store.getState()}
      />,
      document.getElementById('root')
    );
  • 相关阅读:
    Java虚拟机及运行时数据区
    Math小记
    利用BitMap进行大数据排序去重
    Java直接内存与堆内存
    【分布式搜索引擎】Elasticsearch之安装Elasticsearch可视化平台Kibana
    【分布式搜索引擎】Elasticsearch之如何安装Elasticsearch
    【微信错误】{"errcode":"40013","errmsg":"invalid appid hint: [mackRA06203114]","success":false}
    【Java】理解ClassNotFoundException与NoClassDefFoundError的区别
    【Spring Boot】Spring Boot之使用AOP实现数据库多数据源自动切换
    【异常】The dependencies of some of the beans in the application context form a cycle
  • 原文地址:https://www.cnblogs.com/Answer1215/p/5181835.html
Copyright © 2011-2022 走看看