从Vue到React/Redux的学习,小黑的我一步一步过来

962 阅读6分钟

我学习react的时间并不长,如果下面有讲得不好,希望大家指点一下,谢谢了。

Vue和React的区别

讲Vue和React的区别的文章太多太多了,这篇文章主要也不是讲这个的,就随便说说。

相同点

  • 都是基于Virtual DOM(虚拟DOM)进行构建的
  • 组件化思想
  • 都支持服务端渲染(什么叫服务端渲染?前端关于seo的个人理解与改造优化实战(基于Vue-ssr)
  • 都有状态管理方案Vue-Vuex(Vuex时Vue专属的),React-Redux(Redux并非React专属,其实之前我也不知道...)
  • 都有生命周期(虽然说生命周期的方法不一样)

不同点

  • Vue时数据双向绑定(Vue2是基于数据劫持-Object.defindPropety,而Vue3是基于代理-Proxy-es6语法),React时单向数据流(修改数据需要使用this.$setState)
  • Vue的推荐写法时Html+Css+Js,而React的推荐写法时都放在Js文件中取编写(即all-in-js)
  • 虚拟DOM的渲染方式不同(对比方式不同)

其他可以自行掘金/百度一下(这里推荐两篇)

个人理解Vue和React区别

vue和react区别

至于使用Vue还是使用React,这个见仁见智。喜欢哪个就用哪个(其实是公司用哪个你就得用哪个)。Vue学习起来比React快,因为很多东西都帮助我们封装好了,React则相反,学习成本相对高,很多东西需要自己去封装(这个是坏处也是好处...)。

React基础

安装使用

首先,得弄个项目吧。不然怎么见到我们熟悉的Hello World。使用npx create-react-app react-demo创建一个项目,然后进行目录并运行,再删除一些有的没的(目前还用不到),大概就下面这样

开始学习

最基础的组件

因为React就是万物皆组件的思想。后面进阶那里可能会说一说高阶组件,也可能不说(因为这个我也不太熟悉,我学React+Redux也就大概一个多星期,有些点还没有学习到)

无状态组件

无状态?lpl选手?哈哈哈,扯远了。无状态组件在react里面指的就是函数组件(无生命周期,后面进阶会讲到hook),它主要是用来接收父组件的数据(props)并定义模板,以展示数据为主,不推荐进行交互/业务操作,因为这样可以保存高复用性,提高渲染性能。

新建一个文件夹(demo01)和一个index.js放在里面(后面就不提这一点了)

//使用jsx的写法必须引入React,Fragment时非必须,Fragment就类似一个占位的,在react渲染到浏览器时,Fragment并不会渲染出来
import React, { Fragment } from 'react';
//所有组件必须以大写字母开头 这里写小写其实也可以,引入时用大写引入就可以,不然react会将小写开头的组件当成原生html标签
function Sateless(props) {
    return (
        //最外层必须只有一个根元素-和Vue的差不多
        <Fragment>
            <div>我就是一个没牌面无状态组件</div>
            {/* 这种是jsx的注释写法
                props是从父组件接收的数据,这里其实叫什么都可以,只是约定俗成叫这个罢了
            */}
            <div>{props.num}</div>
            <div>{props.text}</div>
        </Fragment>
    )
}
//记得导出
export default Sateless;

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
//这里记得大写
import Sateless from './demo01';
ReactDOM.render(
  <React.StrictMode>
  	{/* 传值 */}
    <Sateless num={666} text={'我叫无状态'}/>
  </React.StrictMode>,
  document.getElementById('root')
);

有状态组件

既然有无状态,那就有有状态。不可能所有组件都不推荐进行交互/业务操作吧。有状态组件其实就是类组件(class),有生命周期,支持交互/业务操作,但是渲染开销大(有优必有劣嘛)

写一个经典的demo,+1 -1。哈哈哈

//同样引入React Component可引可不引,不引的话则extends Component要换成 extends React.Component
import React, { Component } from 'react';
class Stateful extends Component {
    constructor(props) {
        super(props);
        //设置初始值
        this.state = {
            count: 0
        }
        //绑定this的第一种写法,在 JavaScript 中,class 的方法默认不会绑定 this。
        //如果你忘记绑定并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
        this.addFunc = this.addFunc.bind(this)
    }
    componentWillMount() {
        console.log('这里就演示一下生命周期,后面再具体讲讲')
        console.log('componentWillMount111')
    }
    componentDidMount() {
        console.log('componentDidMount222')
    }
    addFunc() {
        // 赋值的一种写法
        this.setState({
            count: this.state.count + 1
        })
    }
    subFunc() {
        // 赋值的另一种写法(下面会讲讲为什么使用这种)
        this.setState((preState) => {
            return {
                count: preState.count - 1
            }
        })
    }
    setZero() {
        this.setState({
            count: 0
        })
    }
    render() {
        return (
            <div>
                <div>我是一个有状态的组件</div>
                <div>{this.state.count}</div>
                <div>
                    <button onClick={this.addFunc}>+1</button>
                    {/* 绑定this的第二种写法 */}
                    <button onClick={this.subFunc.bind(this)}>-1</button>
                    {/* 绑定this的第三种写法 */}
                    <button onClick={() => { this.setZero() }}>归0</button>
                </div>
                <div>我也可以接收父组件传的值:{this.props.text}</div>
            </div>
        );
    }
}
export default Stateful;

引入的方式一样,然后大概长这鬼样

setState

上面我们说过,this.setState()有不同的方式,其实也不算。只是它可以接受两个参数,第一个参数可以是一个对象或者一个函数,而第二个参数可以接受一个回调函数或者为空(大多情况下我们都是为空)。看下官方说明,这里提太多,具体看官方,说得比较详细,我也不想把那么多东西直接cv过来。

官方中说的会合并大概是这个意思,下面这三个操作等价于只有一个操作

//看起来像会执行三次,但是其实react会将其合并为一个
testFunc() {
   this.setState({ count: this.state.count + 1 });
   this.setState({ count: this.state.count + 1 });
   this.setState({ count: this.state.count + 1 });
}

官方说了第一个参数的,我们这里说说第二个参数,第二个参数接受一个回调函数,是指当数据更新则会执行这个回调(这样就不会出现那种异步问题,我是这样认为的)

生命周期

  • componentWillMount 在渲染前调用,在客户端也在服务端。

  • componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。

  • componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

  • shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。(这个Vue默认帮我们做了优化)

  • componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

  • componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

  • componentWillUnmount 在组件从 DOM 中移除之前立刻被调用

学过Vue应该大概都知道这些各自代表什么,这里不会详讲,下面如果有提到就具体讲吧。

条件渲染

这里用另一个经典例子,todolist(之后讲redux也是用这个例子)

import React, { Component } from 'react'

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: '',
            todoList: [
                { id: 1, text: '学习Vue' },
                { id: 2, text: '学习React' },
                { id: 3, text: '学习Angular' },
            ]
        }
        this.changeValueFunc = this.changeValueFunc.bind(this)
        this.addNewListItem = this.addNewListItem.bind(this)
        this.delItem = this.delItem.bind(this)
    }
    changeValueFunc(e) {
        const inputValue = e.target.value
        this.setState({
            inputValue
        })
    }
    addNewListItem() {
        if (!this.state.inputValue) {
            return;
        }
        const value = this.state.inputValue;
        this.setState({
            todoList: [...this.state.todoList, { id: this.state.todoList.length + 1, text: value }],
            inputValue: ''
        })
    }
    delItem(index) {
        let newList = this.state.todoList
        newList.splice(index, 1)
        this.setState({
            todoList: newList
        })
    }
    render() {
        let { inputValue, todoList } = this.state
        return (
            <div>
                <input type="text" value={inputValue} onChange={this.changeValueFunc} />
                <button onClick={this.addNewListItem}>放开我,我还要学习</button>
                <ul>
                    {/* 遍历 */}
                    {todoList.map(
                        // 传参用的方法
                        (item, index) => <li onClick={() => { this.delItem(index) }} key={item.id}>{item.text}</li>
                    )}
                </ul>
                {
                    todoList.length >= 5 ? <h1>上号了,别学了</h1> : null
                }
            </div>
        );
    }
}
export default TodoList;

为什么要使用Key???我相信你是知道为什么的(和Vue同理)。不知道的话,可以移步官方说明

基础就讲到这里了,后面如果有再慢慢补上去,主要是我一开始学习笔记不见了,全部重新再写一次还是很烦的,有一些笔记写什么也忘记了。

React进阶

插槽

普通用法(匿名插槽)

import React, { Component } from 'react'
class Slot extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    render() {
        console.log(this.props.children)
        return (
            <div>
                <div>我是本身的的</div>
                下面就是插槽
                {/* react是通过props.children来获取插槽内容 */}
                {this.props.children}
            </div>
        );
    }
}
export default Slot;

scr/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Slot from './demo05';

ReactDOM.render(
  <React.StrictMode>
    <Slot>
      <div>我是第一个传过去的内容</div>
      <div>我是第二个传过去的内容</div>
      <div>我是第三个传过去的内容</div>
    </Slot>
  </React.StrictMode>,
  document.getElementById('root')
);

进阶用法(具名插槽)

具名插槽其实是利用this.props.children中子组件的某个属性来判断实现的

import React, { Component } from 'react'
class Slot extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    render() {
        let nameSlot = {};
        // 提取具名插槽
        this.props.children.forEach(item => {
            if (item.props.slot) {
                nameSlot[item.props.slot] = item;
            }
        })
        return (
            <div>
                <div style={{ color: 'red' }}>
                    {nameSlot['nameSlotOne']}
                </div>
                {/* 过滤非具名插槽 */}
                {this.props.children.filter(item => !item.props.slot)}
                <div style={{ color: 'red' }}>
                    {nameSlot['nameSlotTwo']}
                </div>
            </div>
        );
    }
}
export default Slot;

scr/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Slot from './demo05';

ReactDOM.render(
  <React.StrictMode>
    <Slot>
      <div>我是第一个匿名插槽</div>
      <div>我是第二个匿名插槽</div>
      <div>我是第三个匿名插槽</div>
      <div slot="nameSlotOne">我是第一个具名插槽</div>
      <div slot="nameSlotTwo">我是第一个具名插槽</div>
    </Slot>
  </React.StrictMode>,
  document.getElementById('root')
);

hook

什么是hook?或者说为什么使用hook?官方时这样说的

它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

hook官方说明

因为我们知道在无状态组件中时没有生命周期和this.setState()等方法的,而hook就帮助我们实现了这种方法(hook也只可以在无状态组件中使用,不可以在class组件中使用,会报错)。这里我就讲讲几个简单的hook,因为有的我有的还在看......

useState

这个可以理解为我们在有状态组件中使用的this.setState(),所以用hook重新写一下那个+1 -1的例子

//记得引入useState
import React, { useState } from 'react'
function HookC() {
    //useState接收一个参数,就是初始值,下面是es6解构赋值的写法,useState()会返回一个数组
    const [count, setCount] = useState(0)
    //es5写法
    //   const countArr = useState(0)
    //   const count = countArr[0]
    //   const setCount = countArr[1]
    return (
        <div>
            <div>{count}</div>
            <button onClick={() => { setCount(count + 1) }}>+1</button>
            <button onClick={() => { setCount(count - 1) }}>-1</button>
            <button onClick={() => { setCount(0) }}>归0</button>
        </div>
    )
}
export default HookC

代码是不是看起来少了很多

useEffect

官方说明

如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

要记住一点

useEffect 会在每次渲染后都执行吗? 是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。(我们稍后会谈到如何控制它。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕

我们直接在上面那个的基础继续写

	//加上这一块在函数内
    useEffect(()=>{
        console.log('加载成功')
    })

我们发现好像执行了很多次,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。所以我们需要对它进行控制 代码改为

    useEffect(()=>{
        console.log('加载成功')
    },[])

我们现在发现,不会再出现刚刚那种情况了,因为第二个参数是表示当数组里面的值改变就会触发,但是现在数组里面没有值,如果要触发可以这样写

    useEffect(()=>{
        console.log('加载成功')
    },[count])

路由

react路由和vue一样有history和hash模式,两者区别请自行掘金/百度,下面是以hash模式为例

安装

npm install react-router-dom --save

基础用法

新建三个文件demo07/index.js、demo07/about.js、demo07/main.js

demo07/index.js

import React from 'react'
import {HashRouter as Router,Route,Link} from 'react-router-dom'
import AboutC from './about'
import Mainc from './main'
function RouterC(){
    return(
        <Router basename='/RouterC'>
            <div>我是RouterC页面</div>
            <Link to='/AboutC'>去about页面</Link>
            <Link to='/Mainc'>去mian页面</Link>
            <Route path='/AboutC' component={AboutC}></Route>
            <Route path='/Mainc' component={Mainc}></Route>
        </Router>
    )
}
export default RouterC

demo07/about.js

import React,{Component} from 'react'
class AboutC extends Component {
    constructor(props) {
        super(props);
    }
    //组件被移除时会触发
    componentWillUnmount(){
        console.log('我是AboutC页面,我被移除了')
    }
    render() { 
        return ( 
            <h1>我是AboutC页面</h1>
         );
    }
}
export default AboutC;

demo07/main.js

import React from 'react'
function MainC(){
    return(
        <h1>我是Main页面</h1>
    )
}
export default MainC

scr/index.js

import React from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from './App'
import RouterC from "./demo07"
import { HashRouter as Router, Route, Link } from 'react-router-dom'
ReactDOM.render(
  <Router>
    <div><Link to='/'>首页</Link></div>
    <div><Link to='/RouterC'>RouterC页面</Link></div>
    <Route path='/' exact component={App}></Route>
    <Route path='/RouterC' component={RouterC}></Route>
  </Router>,
  document.getElementById("root")
)

我们在scr/index.js中使用exact,这个是什么意思?

exact是Route的一个属性,认为其是一种严格匹配模式
当exact为false时,根据路由匹配所有组件,如"/" 匹配 “/”、“/home”、“/home/menu”
当exact为true时,则“/” 仅匹配“/”、无法匹配到“/home”。

假如去掉exact,就会变成下面这个

进阶用法(传参)

改一下上面demo07的三个文件 index.js

<Router basename='/RouterC'>
    <div>我是RouterC页面</div>
    <Link to='/AboutC/838182202'>去about页面</Link>
    <Link to='/Mainc?id=838182202'>去mian页面</Link>
    <Route path='/AboutC/:id' component={AboutC}></Route>
    <Route path='/Mainc' component={Mainc}></Route>
</Router>

about.js

import React,{Component} from 'react'
class AboutC extends Component {
    constructor(props) {
        super(props);
        console.log(this.props)
    }
    componentWillUnmount(){
        console.log('我是AboutC页面,我被移除了')
    }
    render() { 
        return ( 
            <div>
                <h1>我是AboutC页面</h1>
                <div>我通过路由传参过来的:id是{this.props.match.params.id}</div>
            </div>
            
         );
    }
}
export default AboutC;

main.js

import React from 'react'

function MainC(props){
    console.log(props.location.search)
    const id = GetQueryValue1(props.location.search,'id')
    return(
        <div>
            <h1>我是Main页面</h1>
            <div>我通过路由传参过来的:id是{id}</div>
        </div>
    )
}
function GetQueryValue1(url,queryName,) {
    let reg = new RegExp("(^|&)" + queryName + "=([^&]*)(&|$)", "i");
    let r = url.substr(1).match(reg);
    if ( r != null ){
       return decodeURI(r[2]);
    }else{
       return null;
    }
 }
export default MainC

其他请掘金/百度 其他参考文章

Redux的使用

本来想另起一篇文章来讲的,但是又觉得放在一起好像更好,所以就放在一起了。

是什么?为什么要用?

我这里引用一下阮一峰大神关于redux的博客的话

首先明确一点,Redux 是一个有用的架构,但不是非用不可。事实上,大多数情况,你可以不用它,只用 React 就够了。 曾经有人说过这样一句话。
"如果你不知道是否需要 Redux,那就是不需要它。"
"只有遇到 React 实在解决不了的问题,你才需要 Redux 。"

简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不>必要的,用了反而增加复杂性。
用户的使用方式非常简单
用户之间没有协作
不需要与服务器大量交互,也没有使用 WebSocket
视图层(View)只从单一来源获取数据
**上面这些情况,都不需要使用 Redux。 **

用户的使用方式复杂
不同身份的用户有不同的使用方式(比如普通用户和管理员)
多个用户之间可以协作
与服务器大量交互,或者使用了WebSocket
View要从多个来源获取数据
上面这些情况才是 Redux 的适用场景:多交互、多数据源。
**从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。 **

某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态

基本用法

安装

npm install redux

使用

在src目录新建一个store.js

import { createStore } from 'redux';
//设置默认值
const defaultState = {
    count: 0
}
// 处理器
const reducerFunc = (state = defaultState, action) => {
    let newState = JSON.parse(JSON.stringify(state))
    switch (action.type) {
        case 'add_num': newState.count = state.count + 1;
            return newState;
        case 'sub_num': newState.count = state.count - 1;
            return newState;
        case 'zero_num': newState.count = 0;
            return newState;
        default: return newState
    }
}
const store = createStore(reducerFunc);
export default store

我们以计数器为例

import React,{Component} from 'react'
import store from '../store'
class CountC extends Component {
    constructor(props) {
        super(props);
        //stroe获取值的方法
        this.state = store.getState();
        //当stroe数据变化时触发
        store.subscribe(()=>{
            this.setState( store.getState()) 
        })
    }
    addFunc(){
        // 派发action
        store.dispatch({
            type:'add_num',
        })
    }
    subFunc(){
        store.dispatch({
            type:'sub_num',
        })
    }
    zeroFunc(){
        store.dispatch({
            type:'zero_num',
        })
    }
    render() { 
        return ( 
            <div>
                <div>{this.state.count}</div>
                <button onClick={this.addFunc.bind(this)}>+1</button>
                <button onClick={this.subFunc.bind(this)}>-1</button>
                <button onClick={this.zeroFunc.bind(this)}>归0</button>
            </div>
         );
    }
}
export default CountC;

src/index.js

import React,{Component} from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from './App'
import CountC from "./demo08"
import store from './store'
class TestC extends Component {
  constructor(props) {
    super(props);
    this.state = store.getState();
    store.subscribe(()=>{
        this.setState( store.getState()) 
    })
  }
  render() { 
    return ( 
        <div>我获取的数据和上面的同步{this.state.count}</div>
     );
  }
}
ReactDOM.render(
  <div>
    <div><CountC></CountC></div>
    <TestC></TestC>
  </div>,
  document.getElementById("root")
)

代码优化

上面那样写是可以,但是当我们的处理器reducer多了起来呢,并且action.type也要用常量来定义,不然到时候写错连报错都没有,以及我们创建action最好也另起一个文件。所以,我们在demo文件夹下新建一个store文件,专门负责它自己的store管理 先看看目录 actionType.js

export const ADD_NUM = 'ADD_NUM';
export const SUB_NUM = 'SUB_NUM';
export const ZERO_NUM = 'ZERO_NUM';

createAction.js

//创建本身需要的action
import { ADD_NUM, SUB_NUM, ZERO_NUM } from './actionType'
export const addNumAc = () => {
    return {
        type: ADD_NUM
    }
}
export const subNumAc = () => {
    return {
        type: SUB_NUM
    }
}
export const zeroNumAc = () => {
    return {
        type: ZERO_NUM
    }
}

reducer.js

// 处理本身的,不负责其他人的
import { ADD_NUM, SUB_NUM, ZERO_NUM } from './actionType'
const defaultState = {
    count: 0
}
export const countReducer = (state = defaultState, action) => {
    let newState = JSON.parse(JSON.stringify(state))
    switch (action.type) {
        case ADD_NUM: newState.count = state.count + 1;
            return newState;
        case SUB_NUM: newState.count = state.count - 1;
            return newState;
        case ZERO_NUM: newState.count = 0;
            return newState;
        default: return newState
    }
}

store/index.js

//负责导出所有文件
import * as actionType from './actionType'
import * as creactAction from './createAction'
import { countReducer } from './reducer'
export default countReducer;

计数器部分修改

    constructor(props) {
        super(props);
        //stroe获取值的方法
        this.state = store.getState().countReducer;
        //当stroe数据变化时触发
        store.subscribe(()=>{
            this.setState( store.getState().countReducer) 
        })
    }
    addFunc(){
        // 派发
        store.dispatch(addNumAc())
    }
    subFunc(){
        store.dispatch(subNumAc())
    }
    zeroFunc(){
        store.dispatch(zeroNumAc())
    }

src/store.js

import { createStore, combineReducers } from 'redux';
import countReducer from './demo08/store'
// combineReducers时用来连接多个reducer的  我们同时可以起别名({ a:countReducer })这样写
const allReducer = combineReducers({ countReducer })
//下面的第二个参数是我使用Chrome插件配置,如果没有可以直接去掉
const store = createStore(allReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store

运行一下,正常ok。

中间件 redux-thunk

这一节我们换成todolist来说明 安照上节一样修改代码

修改一下createAction.js的代码(我们加点异步操作)

import { ADD_ITEM, DEL_ITEM, CHANGE_VALUE } from './actionType'
export const changeValueAC = inputValue => {
    setTimeout(() => {
        return {
            type: CHANGE_VALUE,
            inputValue
        }
    }, 1000)

}
export const addItemAc = () => {
    return {
        type: ADD_ITEM
    }
}
export const delItemAc = index => {
    return {
        type: DEL_ITEM,
        index
    }
}

然后发现页面就报下面的错误了

Actions must be plain objects. Use custom middleware for async actions.

翻译过来就是 动作必须是普通的对象。使用自定义中间件进行异步操作 所以,我们redux-thunk就是用来解决这个问题的

安装

npm install redux-thunk

使用

src/store.js

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import countReducer from './demo08/store'
import todoListReducer from './demo09/store'
// combineReducers时用来连接多个reducer的  我们同时可以起别名({ a:countReducer })这样写
const allReducer = combineReducers({ countReducer, todoListReducer })
const composeEnhancers =
    typeof window === 'object' &&
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
        }) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(thunk),
);

const store = createStore(allReducer, enhancer);
//如果没有用插件直接下面这样写就可以
// const store = createStore(allReducer, applyMiddleware(thunk));
export default store

createAction.js修改

export const changeValueAC = inputValue => {
    return dispatch => {
        setTimeout(() => {
        	console.log('1秒')
            dispatch({
                type: CHANGE_VALUE,
                inputValue
            })
        },1000)
    }
}

todolist

    changeValueFunc(e) {
        const inputValue = e.target.value
        store.dispatch(changeValueAC(inputValue))
    }

react-redux

react-redux是Redux的作者封装了一个React专用的库,下面三个方法介绍可以直接去阮一峰大神的博客,我这里不做过多介绍。

connect()

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

mapStateToProps()

mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。

mapDispatchToProps()

mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。

使用

我们继续在上面的例子的基础上进行修改。 todolist

import React, { Component } from 'react'
import {connect} from 'react-redux'
import {changeValueAC,addItemAc,delItemAc} from './store/createAction'

class TodoList extends Component {
    render() {
        let { inputValue, todoList } = this.props
        return (
            <div>
                <input type="text" value={inputValue} onChange={this.props.changeValueFunc} />
                <button onClick={this.props.addNewListItem}>放开我,我还要学习</button>
                <ul>
                    {todoList.map(
                        (item, index) => <li onClick={() => { this.props.delItem(index) }} key={item.id}>{item.text}</li>
                    )}
                </ul>
                {
                    todoList.length >= 5 ? <h1>上号了,别学了</h1> : null
                }
            </div>
        );
    }
}
const mapStateToProps = (state)=>{
    let {inputValue,todoList} = state.todoListReducer
    return {
        inputValue,todoList
    }
}
const mapDispatchToProps=(dispatch,ownProps)=>{
    return {
        changeValueFunc(e) {
            const inputValue = e.target.value
            dispatch(changeValueAC(inputValue))
        },
        addNewListItem() {
            dispatch(addItemAc());
        },
        delItem(index) {
            dispatch(delItemAc(index));
        }
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(TodoList);

溜了溜了,就先写到这里,后续看情况继续补充