我理解的todos(redux+react-redux)
demo地址:
git clone https://github.com/reactjs/redux.git
cd redux/examples/todos
npm install
npm start
3.第一部分(如图)
addTodo.png1.入口文件index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './components/App'
import rootReducer from './reducers'
const store = createStore(rootReducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Provider作用:将store传给组件,与connect配合使用,connect则是将组件与redux关联起来。本质上 Provider 就是给 connect 提供 store 用的。(react-redux提供)
2.组件入口App.js
import React from 'react';
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'
// 使用jsx写法一定要引入react,不然会报错:'React' must be in scope when using JSX react/react-in-jsx-scope
const App = () => {
return (
<div>
<AddTodo />
<VisibleTodoList />
</div>
)
}
export default App;
3.AddTodo 的action以及reducer
//action.js
let nextTodoId = 0;
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
//id存在的意义是作为key
id: nextTodoId++,
text
}
}
//reducer
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
]
default:
return state
}
}
export default todos
上面我们已经将reducer作为参数传入了store,所以只要触发dipatch,就会根据type做出相应的更新。
...state解构写法,一定要给state一个默认值,不然会报错。
4.组件AddTodo.js
import React from 'react'
import { addTodo } from '../actions'
import { connect } from 'react-redux'
//{dispatch}解构写法,也可以写成任意一个变量名,比如a,获取dipatch:a.dipatch
const AddTodo = ({ dispatch }) => {
let input;
return (
<form action="" onSubmit={
(e) => {
e.preventDefault() //阻止默认事件,比如这里点击按钮会刷新页面
dispatch(addTodo(input.value))
input.value = ''
}
}>
<input type="text" ref={node => { input = node }} />
<button type="submit">
addTodo
</button>
</form>
)
}
export default connect()(AddTodo)
action和reducer之间能联系起来的关键是connect,connect将store和组件联系起来,即connect连接视图和数据层。通过provider把store绑定在组件上(provider为connect提供了store),当dispatch 的时候,会更新相对应type的store数据 。(有点乱,对于connect的具体解析可参考https://www.jianshu.com/p/9873d4ccb891(关于react-redux中的connect用法介绍及原理解析))
5.VisibleTodoList.js
import { connect } from 'react-redux'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos) => {
console.log(todos)
return todos
}
const mapStateToProps = state => ({
todos: getVisibleTodos(state.todos)
})
export default connect(
mapStateToProps
)(TodoList)
connect的个人总结:
- connect 连接组件和store
- connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])
- mapStateToProps: mapStateToProps(state,ownProps):stateProps,将store中的数据作为props绑定到组件上
- mapDispatchToProps(dipatch,ownProps):dispatchProps,将action作为props绑定到组件上
第二部分(如图)
todoList.png1.actions 和reducers
//actions
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}
export const toggleTodo = (id) => (
{
type: 'TOGGLE_TODO',
id
}
)
VisibilityFilters是用来判断三种列表显示状态。SHOW_ALL显示全部,SHOW_COMPLETED显示已完成的,SHOW_ACTIVE显示正在进行的
//reducers
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
]
case 'TOGGLE_TODO':
return state.map(todo =>
(todo.id === action.id)
? {...todo, completed: !todo.completed}
: todo
)
default:
return state
}
}
export default todos
2.组件
TodoList.js
import React from 'react'
import Todo from './Todo'
const TodoList = ({ todos, toggleTodo }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => toggleTodo(todo.id)}
/>
)}
</ul>
)
export default TodoList
Todo.js
import React from 'react'
const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
</li>
)
export default Todo
这部分的重点
import { connect } from 'react-redux'
import { toggleTodo, VisibilityFilters } from '../actions'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(t => t.completed)
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(t => !t.completed)
default:
throw new Error('Unknown filter: ' + filter)
}
}
const mapStateToProps = state => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
const mapDispatchToProps = dispatch => ({
toggleTodo: id => dispatch(toggleTodo(id))
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
我的理解:
通过connect把组件与store建立起联系。
传入的第一个参数mapStateToProps,我们将store中的数据作为props绑定到组件中,所以这里我们能拿得到state。
传入的第二个参数mapDispatchToProps,参数是dispatch,则是我们将action作为props绑定到组件中,所以当我们点击todoList 的时候,就会触发dispatch,返回包含对应action的object对象。
回到todoList:todoList的两个参数{todos,toggleTodo}就是这样获取的。通过解构在Todo中拿到了action对应的两个参数{completed,text}