解析react-redux官方案例todo

11 篇文章 0 订阅

我相信很多小伙伴在学习react-redux都会涉及到一个todo的官方案例,不过刚刚开始接触的人可能看十几次都没感觉,现在我本人对它的总结下

一、项目的构建

  • 1、本项目我使用前端脚手架yeoman构建参考
  • 2、创建一个todo项目

    yo react-webpack
  • 3、安装别的依赖包

    npm install redux --save
    npm install react-redux --save
    npm install redux-thunk --save  
  • 4、把刚刚构建的项目运行

    npm run serve
  • 5、在浏览器上访问

    localhost:8000

二、运行后项目效果图

这里写图片描述

三、项目组件拆分

  • 1、上面输入框与添加按钮拆分为一个组件AddTodo
  • 2、中间显示的拆分一个组件TodoList
  • 3、底部分类拆分一个组件Link

四、todo项目的开发

  • 1、项目结构(关心我圈住的就可以)

    这里写图片描述

  • 2、根据redux构建项目的方式创建几个文件夹如不清楚请参考
  • 3、开发流程(一般开发流程)

    这里写图片描述

五、AddTodo组件的开发

  • 1、创建action(如果项目大,可以一个组件写一个action.js)

    /**
     * 定义todo项目的action
     */
    'use strict';
    let nextTodo = 0;
    
    // 一般外面需要使用的会定义常量
    export const ADD_TODO = 'ADD_TODO';
    export const TOGGLE_TODO = 'TOGGLE_TODO';
    // 定义一个添加的action
    export const addTodo = (text) => ({
        type: ADD_TODO,
        id: nextTodo++,
        text
    });
    
    // 定义一个切换的action
    export const toggleTodo = (id) => ({
        type: TOGGLE_TODO,
        id
    })
  • 2、AddTodo组件的reducer的书写

    项目需要可以一个组件写一个reducer.js然后用combineReducers合并在一起

    import { combineReducers } from 'redux';
    import { ADD_TODO, TOGGLE_TODO} from './../actions/index';
    
    const todos = (state = [], action) => {
        switch (action.type) {
            case ADD_TODO:
                return [
                    ...state,
                    {
                        id: action.id,
                        text: action.text,
                        completed: false
                    }
                ];
            case TOGGLE_TODO:
                // 定义切换的,如果当前点击的id等于传递进来的id就改变状态,否则就不边
                return state.map(todo => {
                    if (todo.id === action.id) {
                        return { ...todo, completed: !todo.completed }
                    } else {
                        return todo;
                    }
                })
            default:
                return state;
        }
    }
  • 3、书写展示组件

    ...
    render() {
        // console.log(this.props);
        return (
            <div>
                <input type="text" placeholder="请输入内容" ref="myinput"/>
                <input type="button" value="添加" onClick={this.addTodo} />
            </div>
        )
    }
    ...
  • 4、容器组件(如果对块还有不清楚请参考)

    'use strict'
    /**
     * 定义AddTodo的容器组件
     */
    import {connect} from 'react-redux';
    import AddTodo from './../components/AddTodo';
    // 这样全部的导入的就有点浪费,在AddTodo中只用了一个addTodo方法
    // import * as ActionCreators from './../actions/index';
    import {addTodo} from './../actions';
    export default connect(
        state=>({todos:state.todos}),
        // ActionCreators
        dispatch =>({
            addTodo:(text)=>dispatch(addTodo(text))
        })
    )(AddTodo);
  • 5、容器组件传递了todosaddTodo到展示组件,再看展示组件的完整代码

    /**
     * AddTodo展示组件
     */
    'use strict'
    
    import React, { Component } from 'react';
    
    
    export default class AddTodo extends Component {
        constructor(props) {
            super(props);
            this.addTodo = this.addTodo.bind(this);
        }
        render() {
            // console.log(this.props);
            return (
                <div>
                    <input type="text" placeholder="请输入内容" ref="myinput"/>
                    <input type="button" value="添加" onClick={this.addTodo} />
                </div>
            )
        }
        // 增加的todo的方法
        addTodo() {
            const { addTodo } = this.props;
            // 获取到input的dom节点
            let myinputDom = this.refs.myinput;
            // 发送完成后就清空输入框
            addTodo(myinputDom.value);
            myinputDom.value = '';
        }
    }
    
    // 约束数据类型
    AddTodo.protoTypes = {
        addTodo: React.PropTypes.func.isRequired
    }
  • 6、组装到项目中试跑(本项目中创建了一个入口的App.js来统一管理)

    import 'core-js/fn/object/assign';
    import React from 'react';
    import ReactDOM from 'react-dom';
    import {createStore,applyMiddleware} from 'redux';
    import thunk from 'redux-thunk';
    import {Provider} from 'react-redux';
    import todoApp from './reducers/index';
    import App from './components/App';
    
    let store = createStore(todoApp,applyMiddleware(thunk));
    
    // Render the main component into the dom
    ReactDOM.render(
        <Provider store={store}>
            <App />
        </Provider>
    , document.getElementById('app'));
    /**
     * 主文件入口
     */
    'use strict'
    import React, { Component } from 'react';
    import AddTodoConnect from './../containers/AddTodoConnect';
    
    
    export default class App extends Component {
        constructor(props){
            super(props);
        }
    
        render() {
            return (
                <div>
                    <AddTodoConnect/>
                </div>
            )
        }
    }

六、显示添加的todo组件

  • 1、展示组件(就是接受props数组便利出来)

    /**
     * todo的展示组件
     */
    'use strict'
    import React, { Component } from 'react';
    
    // 引入样式
    require('styles/todolist.css');
    
    export default class TodoList extends Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            const { todos, toggleTodo } = this.props;
            return (
                <ul>
                    {
                        todos.map(todo => (
                            // 注意这个地方不能直接写onClik = {toggleTodo},这样的意思是立即执行
                            //<li key={todo.id} onClick={()=>toggleTodo(todo.id)} style={{
                            //textDecoration: todo.completed ? 'line-through' : 'none',
                            //color: todo.completed ? 'red' : 'black'
                            //}}>{todo.text}</li>
                            <li key={todo.id} onClick={() => toggleTodo(todo.id)} className={todo.completed ? 'list-select' : 'list'}>{todo.text}</li>
                        ))
                    }
                </ul>
            )
        }
    }
    
    // 约束数据类型
    TodoList.protoTypes = {
        todos: React.PropTypes.object.isRequired,
        toggleTodo:React.PropTypes.func.isRequired
    }
  • 2、对应的容器组件

    /**
     * TodoList的容器组件
     */
    'use strict'
    
    import { connect } from 'react-redux';
    import TodoList from './../components/TodoList';
    import { toggleTodo } from './../actions/index';
    
    export default connect(
        state => ({ todos: state.todos}),
        dispatch => ({
            toggleTodo: (id) => dispatch(toggleTodo(id))
        })
    )(TodoList);
  • 3、组装到App.js

七、底部的组件

  • 1、在action里面添加

    ...
    export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
    export const SHOW_ALL = 'SHOW_ALL';
    // 定义一个是否显示的action,每次发送出去的的type是一样的
    export const setVisibilityFilter = (filter) => ({
        type: SET_VISIBILITY_FILTER,
        filter
    })
  • 2、在reducer里面添加

    // 定义一个底部选择的reducer
    const visibilityFilter = (state = SHOW_ALL,action) => {
        switch(action.type){
            case SET_VISIBILITY_FILTER:
                return action.filter
            default:
                return state
        }
    }
    
    // 利用redux里面的combineReducers合并所有的reducer
    const todoApp = combineReducers({
        todos,
        visibilityFilter
    })
    // 默认导出
    export default todoApp;
  • 3、定义展示组件

    /**
     * 定义一个底部现在的展示组件
     */
    'use strict'
    import React, { Component } from 'react'
    // 引入样式
    require('styles/link.css');
    
    export default class Link extends Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            const { active, children, onClick } = this.props;
            if (active) {
                return <span>{children}</span>
            } else {
                return (
                    <a href="javascript:void(0)" onClick={()=>onClick()}>{children}</a>
                )
            }
    
        }
    }
  • 4、定义容器组件(关于ownProps参考)

    /**
     * 定义Link的容器组件
     */
    
    import { connect } from 'react-redux';
    import Link from './../components/Link.js';
    import { setVisibilityFilter } from './../actions/index';
    
    
    const mapStateToProps = function(state, ownProps){
        console.log('ownProps--->',ownProps);
        return {
            active: ownProps.filter === state.visibilityFilter
        }
    }
    
    const mapDispatchToProps = (dispatch, ownProps) => ({
        onClick: () => {
            dispatch(setVisibilityFilter(ownProps.filter))
        }
    })
    
    /**下面这样也可以
     * (state, ownProps) => ({
            active: ownProps.filter === state.visibilityFilter
        }),
        (dispatch,ownProps) => ({
            onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
        })
     */
    export default connect(
        mapStateToProps,
        mapDispatchToProps
    )(Link);
    
  • 5、修改之前显示的todo组件(容器组件)

    **
     * 定义一个过滤的方法
     */
    const getVisibleTodos = (todos, filter) => {
        switch(filter){
            case 'SHOW_ALL':
                return todos;
            case 'SHOW_ACTIVE': //未完成
                return todos.filter(todo => !todo.completed);
            case 'SHOW_COMPLETED': // 已完成
                return todos.filter(todo => todo.completed);
            default:
                return todos;
        }
    }
    export default connect(
        // 传递到展示组件的数据过滤了
        state => ({ todos: getVisibleTodos(state.todos,state.visibilityFilter)}),
        dispatch => ({
            toggleTodo: (id) => dispatch(toggleTodo(id))
        })
    )(TodoList);

八、demo下载地址

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水痕01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值