Redux - TodoList

用ts 跟了一遍TodoList.
先总结下基本的收获

  1. Action - 必须带个type字段. 是用来表述发生了什么和发生变化的数据
    { type: ADD_TODO, id: nextTodoId++, text };
  2. Action Creator - 就是对Action做个封装的function
    export let addTodo = (text: string) => { return { type: ADD_TODO, id: nextTodoId++, text }; }
  3. Reducer - 根据action不同对state的更新
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 Object.assign({}, state, {
completed: !state.completed
})

default:
return state
}
}
  1. CombineReducer - 对多个reducer的整合,store只接受一个参数

export const todoAppReducer = combineReducers({ visibilityFilter, todos })

  1. Store
    • Holds application state;
    • Allows access to state via getState();
    • Allows state to be updated via dispatch(action);
    • Registers listeners via subscribe(listener);
    • Handles unregistering of listeners via the function returned by subscribe(listener).
    • It’s important to note that you’ll only have a single store in a Redux application.
    • When you want to split your data handling logic, you’ll use reducer composition instead of many stores.

组建分类:

  • Container
  • Component

Component其实就是无状态组建,所有的绑定都是根据props来的

import * as React from 'react';

interface LinkProp {
active: boolean;
children?: React.ReactNode;
onClick: () => any;
}

export class Link extends React.Component<LinkProp, any> {

constructor() {
super();
}
render() {
if (this.props.active) {
return (
<span>{this.props.children}</span>
)
}

return (
<a
href="#"
onClick={e => {
e.preventDefault();
this.props.onClick();
} }
>
{this.props.children}
</a>
)

}
}

Container是会根据state变化做出重新render的容器组建

import * as React from 'react';
import {connect, IMapStateToProps, IMapDispatchToProps} from 'react-redux';
import {addTodo} from './../actions';




interface AddTodoProp {
addToDoClick?: (string) => void
}


const mapDispatchToProps: IMapDispatchToProps = (dispatch) => {
return {
addToDoClick: (text) => {
dispatch(addTodo(text))
}
}
}

@connect(undefined, mapDispatchToProps)
export class AddTodo extends React.Component<AddTodoProp, any> {
input: HTMLInputElement;
constructor() {
super();
}

render() {
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!this.input.value.trim()) {
return
}
this.props.addToDoClick(this.input.value);
this.input.value = ''
} }>
<input ref={node => {
this.input = node
} } />
<button type="submit">
Add Todo
</button>
</form>
</div>
)

}
}

React-Redux

connect

就一个作用把state里的字段自动赋值给组建的props

两个主要方法:

const mapStateToProps: IMapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
}


const mapDispatchToProps: IMapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter))
}
}
}

用来作用在container 组建上

@connect(mapStateToProps, mapDispatchToProps)
export class FilterLink extends React.Component<FilterLinkProp, any> {

constructor() {
super();
}
render() {
return (
<Link active={this.props.active}
onClick={this.props.onClick}
>
{this.props.children}
</Link>
)

}
}

目前ts还不支持 object spread 语法,所以写起来郁闷点.
还有个坑就是注意,如果props.childer 容易忘

Provider

就一个作用减少自己每个组建传递store的麻烦.只能包裹一个根组建

export class App extends React.Component<any, any> {
constructor() {
super();
}

render() {
return (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)
}
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('targetreact'));

Source Code: https://github.com/dreambo8563/react-Tutorial/tree/master/app