寫在開始
前篇中爱只,我們介紹了 Redux 画畅,對 Store 、 Action 渣锦、Reducer 有了初步的了解诚些。并配合 react-redux 飞傀,梳理了其工作流程皇型,結(jié)合使用了 Provider 、 connect() 砸烦。
接下來弃鸦,我們完成開篇任務(wù)中的最后一項,**過濾顯示 TODO **幢痘。
源碼:https://github.com/eylu/web-lib/tree/master/ReactReduxDemo/app_step3
開發(fā)
我們所做任務(wù)如下:
- 在入口文件添加一個新的初始
state
:
與todos
平級唬格,做為當前過濾條件。 - 創(chuàng)建一個子組件
TodoFilterComponent
:
包含3個過濾按鈕All
颜说、Undo
购岗、Finish
,并接收一個屬性為當前過濾條件门粪,過濾按鈕與當前過濾條件相同時喊积,過濾按鈕不可點擊。 - 修改容器組件
HomeContainer
:
引入過濾組件玄妈,并且可以設(shè)置當前過濾條件乾吻,根據(jù)過濾條件顯示不同狀態(tài)的 TODO 項。 - 添加
Action
:
定義ActionCreator
生成設(shè)置當前過濾條件的Action
拟蜻。 - 添加
Reducer
:
可以設(shè)置當前的過濾條件绎签。
1、添加新初始 state
ReactReduxDemo/app/index.js
文件瞭郑,修改如下:
import React, { Component } from 'react';
import {
View,
StyleSheet,
} from 'react-native';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducers from './reducers/index';
import HomeContainer from './containers/home.container';
// 這是初始數(shù)據(jù)
const initState = {
todos: [
{title:'吃早飯',status:true},
{title:'打籃球',status:false},
{title:'修電腦',status:false},
],
filter: 'All', // 'All'|'Undo'|'Finish' // 添加新的初始 `state`
};
let store = createStore(reducers, initState);
export default class RootWrapper extends Component{
render(){
return (
<Provider store={store}>
<View style={styles.wrapper}>
<HomeContainer />
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
wrapper: {
flex: 1,
marginTop: 20,
},
});
2辜御、創(chuàng)建子組件 TodoFilterComponent
新建文件 ReactReduxDemo/app/components/todo-filter.component.js
, 如下:
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
} from 'react-native';
export default class TodoFilterComponent extends Component{
constructor(props){
super(props);
}
filterTodo(filter){
this.props.filterTodo && this.props.filterTodo(filter);
}
renderFilter(filter){
if(filter==this.props.filter){
return (
<Text style={[styles.filter,styles.filterCurrent]}>{filter}</Text>
);
}
return (
<TouchableOpacity onPress={()=>{this.filterTodo(filter)}}>
<Text style={styles.filter}>{filter}</Text>
</TouchableOpacity>
);
}
render(){
return (
<View style={styles.wrapper}>
{this.renderFilter('All')}
{this.renderFilter('Undo')}
{this.renderFilter('Finish')}
</View>
);
}
}
const styles = StyleSheet.create({
wrapper: {
flexDirection: 'row',
paddingLeft: 20,
paddingTop: 20,
},
filter: {
marginRight: 20,
textDecorationLine: 'underline',
},
filterCurrent:{
color: 'gray',
textDecorationLine: 'none',
},
});
3屈张、容器組件HomeContainer
我們在其中引入子組件TodoFilterComponent
擒权,并添加過濾方法,此方法的功能為設(shè)置當前過濾條件阁谆。
ReactReduxDemo/app/containers/home.container.js
文件碳抄,修改如下:
import React, { Component } from 'react';
import {
View,
Text
} from 'react-native';
import { connect } from 'react-redux';
import { changeTodoStatus, addNewTodo, filterTodoList } from '../actions/index'; // 引入 action
import TodoFormComponent from '../components/todo-form.component';
import TodoListComponent from '../components/todo-list.component';
import TodoFilterComponent from '../components/todo-filter.component'; // 引入子組件
class HomeContainer extends Component{
constructor(props){
super(props);
}
addTodo(text){
let { dispatch } = this.props;
dispatch(addNewTodo(text));
}
toggleTodo(index){
let { dispatch } = this.props;
dispatch(changeTodoStatus(index));
}
filterTodo(filter){
let { dispatch } = this.props; // 從 props 里解構(gòu)出 dispatch
dispatch(filterTodoList(filter)); // 執(zhí)行過濾方法
}
render(){
return (
<View>
<TodoFormComponent addTodo={(text)=>{this.addTodo(text)}} />
<TodoListComponent todoList={this.props.todoList} toggleTodo={(index)=>{this.toggleTodo(index)}} />
<TodoFilterComponent filter={this.props.currentFilter} filterTodo={(filter)=>{this.filterTodo(filter)}} /> // 渲染子組件(注釋會報錯,請刪除注釋)
</View>
);
}
}
// 基于全局 state 场绿,哪些 state 是我們想注入的 props
function mapStateToProps(state){
return {
todoList: state.todos,
currentFilter: state.filter, // 注入新的 state 到 props
}
}
export default connect(mapStateToProps)(HomeContainer);
4剖效、修改 Action
添加新的 action
類型,新的 action
創(chuàng)建函數(shù)焰盗。
ReactReduxDemo/app/actions/index.js
文件璧尸,修改如下:
/*********************************** action 類型常量 *************************************/
/**
* 更改 TODO 狀態(tài)
* @type {String}
*/
export const TOGGLE_TODO_STATUS = 'TOGGLE_TODO_STATUS';
export const ADD_NEW_TODO = 'ADD_NEW_TODO';
export const SET_FILTER = 'SET_FILTER'; // 添加新的 action 類型
/*********************************** action 創(chuàng)建函數(shù) *************************************/
/**
* 更改 TODO 狀態(tài)
* @param {Number} index TODO索引
* @return {Object} action
*/
export function changeTodoStatus(index){
return {type: TOGGLE_TODO_STATUS, index};
}
export function addNewTodo(text){
return {type: ADD_NEW_TODO, text};
}
export function filterTodoList(filter){ // 添加新的 action 創(chuàng)建函數(shù)
return {type: SET_FILTER, filter};
};
5、修改 Reducer
引用新的action
類型熬拒,添加新的reducer
函數(shù)爷光,并在 combineReducers
中使用。
ReactReduxDemo/app/reducers/index.js
文件澎粟,修改如下:
import { combineReducers } from 'redux';
import { TOGGLE_TODO_STATUS, ADD_NEW_TODO, SET_FILTER } from '../actions/index'; // 引入 action 蛀序,使用 action 類型常量
function todoList(state=[], action){
switch(action.type){
case TOGGLE_TODO_STATUS:
var todo = state[action.index];
return [
...state.slice(0, action.index),
Object.assign({}, todo, {
status: !todo.status
}),
...state.slice(action.index + 1)
];
case ADD_NEW_TODO:
return [
...state,
{
title: action.text,
status: false,
}
];
default :
return state;
}
}
function setFilter(state='', action){ // 定義了新的 reducer
switch(action.type){
case SET_FILTER:
return action.filter;
default :
return state;
}
}
const reducers = combineReducers({
todos: todoList,
filter: setFilter, // 添加了新的 reducer combine
});
export default reducers;
運行項目欢瞪,是否顯示出了過濾按鈕呢?點擊試試看徐裸,能否過濾遣鼓?(暫時還不可以,我們需要寫方法對數(shù)據(jù)進行過濾)
過濾 TODO 顯示
todo 列表有了重贺,當前過濾條件也可以設(shè)置了骑祟。這里,我們需要完成根據(jù)當前過濾條件對 TODO 列表進行過濾顯示檬姥。
我們知道曾我,TODO 列表的顯示,是根據(jù)容器組件HomeContainer
給的數(shù)據(jù)來顯示的健民,它并不知道數(shù)據(jù)的具體來源抒巢,給它什么數(shù)據(jù)它就顯示什么數(shù)據(jù)(這也正是我們想要的功能)。
我們就會想到秉犹,將容器組件HomeContainer
的 TODO 列表數(shù)據(jù)做修改蛉谜,是不是就可以了呢?是的崇堵,確實是這樣型诚。
我們來看容器組件HomeContainer
部分代碼
// 基于全局 state ,哪些 state 是我們想注入的 props
function mapStateToProps(state){
return {
todoList: state.todos, // <---- 這里就是給子組件展示的數(shù)據(jù)
currentFilter: state.filter,
}
}
我們將其做一些修改過濾鸳劳,就OK了狰贯。
ReactReduxDemo/app/containers/home.container.js
文件,修改如下:
import React, { Component } from 'react';
import {
View,
Text
} from 'react-native';
import { connect } from 'react-redux';
import { changeTodoStatus, addNewTodo, filterTodoList } from '../actions/index';
import TodoFormComponent from '../components/todo-form.component';
import TodoListComponent from '../components/todo-list.component';
import TodoFilterComponent from '../components/todo-filter.component';
class HomeContainer extends Component{
constructor(props){
super(props);
}
addTodo(text){
let { dispatch } = this.props;
dispatch(addNewTodo(text));
}
toggleTodo(index){
let { dispatch } = this.props;
dispatch(changeTodoStatus(index));
}
filterTodo(filter){
let { dispatch } = this.props;
dispatch(filterTodoList(filter));
}
render(){
return (
<View>
<TodoFormComponent addTodo={(text)=>{this.addTodo(text)}} />
<TodoListComponent todoList={this.props.todoList} toggleTodo={(index)=>{this.toggleTodo(index)}} />
<TodoFilterComponent filter={this.props.currentFilter} filterTodo={(filter)=>{this.filterTodo(filter)}} />
</View>
);
}
}
const getFilterTodos = (todos, filter) => { // 定義 TODO 過濾方法赏廓,返回新的數(shù)據(jù)
switch (filter) {
case 'All':
return todos;
case 'Undo':
return todos.filter( todo => !todo.status);
case 'Finish':
return todos.filter( todo => todo.status);
default:
throw new Error('Unknown filter: ' + filter);
}
}
// 基于全局 state 涵紊,哪些 state 是我們想注入的 props
function mapStateToProps(state){
return {
todoList: getFilterTodos(state.todos, state.filter), // 注入props時,調(diào)用 TODO 過濾方法
currentFilter: state.filter,
}
}
export default connect(mapStateToProps)(HomeContainer);
運行項目幔摸,是否可以對 TODO 列表進行過濾了呢摸柄?恭喜你!既忆!
到目前為止驱负,我們的 ReactNative + Redux 開發(fā)示例告一小段落。
后面會有中間件患雇、異步的的使用方法跃脊。