[Redux] Generating Containers with connect() from React Redux (AddTodo)

來源:互聯網
上載者:User

標籤:

Code to be refacted:

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};

 

Instead of passing:

const AddTodo = (props, { store }) => {

We can only pass ‘props‘, and we get ‘dispatch‘ from the props:

let AddTodo = ({ dispatch }) => {

 

Then we will create a container component with connect that will inject that the dispatch function as a prop, we will remove the context types because the component generated by connect function will take care of reading this chore from the context.

The first argument to the connect function is map straight to props, but there aren‘t any props for at to-do component that depend on the current state, so I return an empty object. The second argument to connect is map dispatch to props, but at to-do component doesn‘t need any callback props. It just accepts the dispatch function itself, so I‘m returning it as a prop with the same name.

Finally, I‘m calling the function for a second time to specify the component I want to wrap, in this case, AddTodo itself:

AddTodo = connect(  state => {    return {};  },  dispatch => {    return {dispatch};  })(AddTodo);

 

However, it is wasteful to even subscribe to this chore if we don‘t calculate any props from its state. So I‘m replacing the mapStateToProps function with an null, which tells connect that there is no need to subscribe to this store.

Additionally, it‘s pretty common pattern to inject just the dispatch function. This is why if you specify null or any false value in connect as the second argument, you‘re going to get dispatch injected as a prop

AddTodo = connect(  null,  null)(AddTodo);

 

In fact, I can just remove all arguments here. The default behavior will be to not subscribe to this chore and to inject just the dispatch function as a prop.:

AddTodo = connect()(AddTodo);

 

So the AddTodo function would looks like:

let AddTodo = ({ dispatch }) => {  let input;  return (    <div>      <input ref={node => {        input = node;      }} />      <button onClick={() => {        dispatch({          type: ‘ADD_TODO‘,          id: nextTodoId++,          text: input.value        })        input.value = ‘‘;      }}>        Add Todo      </button>    </div>  );};AddTodo = connect()(AddTodo);

 

 

The AddTodo component that I declare accepts dispatch as a prop, but it doesn‘t know how to get this store. It just hopes that someone is going to pass the dispatch to it.

The connect code without any arguments is going to generate a container component that does not subscribe to this store. However, that will pass dispatch to the component that it wraps. In this case, it wraps my AddTodo component.

The second connect call returns the generated container component. I‘m assigning it to AddTodo. I‘m reassigning the let binding the second time.

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

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>);const { connect } = ReactRedux;let nextTodoId = 0;let AddTodo = ({ dispatch }) => {  let input;  return (    <div>      <input ref={node => {        input = node;      }} />      <button onClick={() => {        dispatch({          type: ‘ADD_TODO‘,          id: nextTodoId++,          text: input.value        })        input.value = ‘‘;      }}>        Add Todo      </button>    </div>  );};AddTodo = connect()(AddTodo);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      );  }}const mapStateToTodoListProps = (state) => {  return {    todos: getVisibleTodos(      state.todos,      state.visibilityFilter    )  };};const mapDispatchToTodoListProps = (dispatch) => {  return {    onTodoClick: (id) => {      dispatch({        type: ‘TOGGLE_TODO‘,        id      });    }  };};const VisibleTodoList = connect(  mapStateToTodoListProps,  mapDispatchToTodoListProps)(TodoList);const TodoApp = () => (  <div>    <AddTodo />    <VisibleTodoList />    <Footer />  </div>);const { Provider } = ReactRedux;const { createStore } = Redux;ReactDOM.render(  <Provider store={createStore(todoApp)}>    <TodoApp />  </Provider>,  document.getElementById(‘root‘));

 

[Redux] Generating Containers with connect() from React Redux (AddTodo)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.