zoukankan      html  css  js  c++  java
  • [Redux] Passing the Store Down Implicitly via Context

    We have to write a lot of boiler plate code to pass this chore down as a prop. But there is another way, using the advanced React feature called context.

    const TodoApp = ({ store }) => (
      <div>
        <AddTodo store={store} />
        <VisibleTodoList store={store} />
        <Footer store={store} />
      </div>
    );
    
    
    const { createStore } = Redux;
    
    ReactDOM.render(
      <TodoApp store={createStore(todoApp)} />,
      document.getElementById('root')
    );

    I'm creating a new component called provider. From its render method, it just returns whatever its child is. We can wrap any component in a provider, and it's going to render that component.

    I'm changing the render call to render a to-do app inside the provider. I'm moving this tool prop from the to-do app to the provider component. The provider component will use the React advanced context feature to make this chore available to any component inside it, including grandchildren.

    To do this, it has to define a special method get child context that will be called by React by using this props tool which corresponds to this chore that is passed to the provider as a prop just once.

    class Provider extends Component {
      getChildContext() {
        return {
          store: this.props.store
        }; 
      }
    
      render() {
        return this.props.children;
      }
    }
    Provider.childContextTypes = {
      store: React.PropTypes.object
    };
    
    const { createStore } = Redux;
    
    ReactDOM.render(
      <Provider store={createStore(todoApp)}>
        <TodoApp />
      </Provider>,
      document.getElementById('root')
    );

    Remember to define 'childContextTypes', if not it won't work.

    Then we go to refactor the 'VisibleTodoList' class Component:

    class VisibleTodoList extends Component {
      componentDidMount() {
        const { store } = this.context;
        this.unsubscribe = store.subscribe(() =>
          this.forceUpdate()
        );
      }
      
      componentWillUnmount() {
        this.unsubscribe();
      }
      
      render() {
        const props = this.props;
        const { store } = this.context;
        const state = store.getState();
        
        return (
          <TodoList
            todos={
              getVisibleTodos(
                state.todos,
                state.visibilityFilter
              )
            }
            onTodoClick={id =>
              store.dispatch({
                type: 'TOGGLE_TODO',
                id
              })            
            }
          />
        );
      }
    }
    
    VisibleTodoList.contextTypes = {
      store: React.PropTypes.object
    };

      

    The same as 'Footer' Class Component:

    class FilterLink extends Component {
      componentDidMount() {
        const { store } = this.context;
        this.unsubscribe = store.subscribe(() =>
          this.forceUpdate()
        );
      }
      
      componentWillUnmount() {
        this.unsubscribe();
      }
      
      render() {
        const props = this.props;
        const { store } = this.context;
        const state = store.getState();
        
        return (
          <Link
            active={
              props.filter ===
              state.visibilityFilter
            }
            onClick={() =>
              store.dispatch({
                type: 'SET_VISIBILITY_FILTER',
                filter: props.filter
              })
            }
          >
            {props.children}
          </Link>
        );
      }
    }
    FilterLink.contextTypes = {
      store: React.PropTypes.object
    };

    Then 'AddTodo' functional component, it doesn't have 'this' keyword, but we still able to get the 'context' from the second arguement.

    let nextTodoId = 0;
    const AddTodo = (props, { store }) => {
      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>
      );
    };
    
    AddTodo.contextTypes = {
      store: React.PropTypes.object
    };

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

    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 { 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() {
        const { store } = this.context;
        this.unsubscribe = store.subscribe(() =>
          this.forceUpdate()
        );
      }
      
      componentWillUnmount() {
        this.unsubscribe();
      }
      
      render() {
        const props = this.props;
        const { store } = this.context;
        const state = store.getState();
        
        return (
          <Link
            active={
              props.filter ===
              state.visibilityFilter
            }
            onClick={() =>
              store.dispatch({
                type: 'SET_VISIBILITY_FILTER',
                filter: props.filter
              })
            }
          >
            {props.children}
          </Link>
        );
      }
    }
    FilterLink.contextTypes = {
      store: React.PropTypes.object
    };
    
    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>
    );
    
    let nextTodoId = 0;
    const AddTodo = (props, { store }) => {
      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>
      );
    };
    AddTodo.contextTypes = {
      store: React.PropTypes.object
    };
    
    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() {
        const { store } = this.context;
        this.unsubscribe = store.subscribe(() =>
          this.forceUpdate()
        );
      }
      
      componentWillUnmount() {
        this.unsubscribe();
      }
      
      render() {
        const props = this.props;
        const { store } = this.context;
        const state = store.getState();
        
        return (
          <TodoList
            todos={
              getVisibleTodos(
                state.todos,
                state.visibilityFilter
              )
            }
            onTodoClick={id =>
              store.dispatch({
                type: 'TOGGLE_TODO',
                id
              })            
            }
          />
        );
      }
    }
    VisibleTodoList.contextTypes = {
      store: React.PropTypes.object
    };
    
    const TodoApp = () => (
      <div>
        <AddTodo />
        <VisibleTodoList />
        <Footer />
      </div>
    );
    
    class Provider extends Component {
      getChildContext() {
        return {
          store: this.props.store
        }; 
      }
    
      render() {
        return this.props.children;
      }
    }
    Provider.childContextTypes = {
      store: React.PropTypes.object
    };
    
    const { createStore } = Redux;
    
    ReactDOM.render(
      <Provider store={createStore(todoApp)}>
        <TodoApp />
      </Provider>,
      document.getElementById('root')
    );
  • 相关阅读:
    Swift中函数
    Swift 中的开关语句switch在swift中的使用
    Swift 函数新特性
    Swift 学习-多线程
    安卓学习
    ios -网络
    ios 中block
    Lua 简易调试
    iOS、Cocos2dx、Unity3D下的坐标系统简介
    Lua开发过程中遇到的一些小问题
  • 原文地址:https://www.cnblogs.com/Answer1215/p/5183679.html
Copyright © 2011-2022 走看看