学习React Hook、Antd与redux笔记

第一节:环境与项目搭建

一. 本地安装node

安装好node后全局安装webpack或yarn

# npm i -g webpack / npm i -g yarn

二. 安装官方create-react-app脚手架并搭建项目

1.全局安装命令

# npm i -g create-react-app
或
# yarn add -g create-react-app

2.安装好后查看版本

# create-react-app --version

3.开始创建项目

# create-react-app demo

创建项目的同时会帮你自动下载依赖,所以不用自己npm install安装依赖,创建完成后直接cd demo去到当前项目npm start 或 yarn start
 

第二节:基础知识

一. 讲解state变量渲染和setState修改数据

1.在组件里面我们通过{}在JSX里面渲染变量
2.如果数据需要修改,并且需要页面同时响应改变,那就需要把变量放在state里面,同时使用setState修改
3.初始化状态state

//初始化状态state
this.state = {
    count: 0
}

4.更新状态使用setState,不能直接this.state.count = xxx 【区别于Vue】

//更新状态使用setState
this.setState({
    count: this.state.count + 1
})

注意事项:

①setState是异步的,底层设计同一个生命周期会批量操作更新state
②setState第二个参数是一个可选参数,传入一个回调函数可以获取到最新的state
③当修改的state依赖上一次修改的state的值时,可以使用以下这种方法修改:

this.setState((prevState, prevProps) => ({
    //prevState:上一次修改的状态state
    //prevProps: 上一次修改的属性props
    count: prevState.count + 1
}), () => {
    //这里可以获取到最新的state
    console.log(this.state.count)
})

二. props属性传递

父组件向子组件传递属性利用props接收,使用例子:

  • 父组件传入参数
import ParentDemo from './ParentDemo'
<ParentDemo title='父组件title信息'></ParentDemo>
  • 子组件使用

1. class组件使用

 import React from 'react'
 export default class ChildDemo extends React.Component {
        render() {
            return (
                <h3>{this.props.title}</h3>
            )
        }
 }

2.函数组件使用

import React from 'react'
export default function ChildDemo(props) {
    return (
        <h3>{props.title}</h3>
    )
}
或接收父组件的参数解构写法
export default function ChildDemo({title}) {
    return (
        <h3>{title}</h3>
    )
}

三. 条件渲染与数据循环

1. 条件渲染写法,一般使用三目表达式

  • 在render函数里使用三目运算:
let result = this.state.showTitle ? <h3>{this.props.title}</h1> : null
{result}
  • 直接使用if else
let result
if(this.state.showTitle) {
    result = {
        <h3>this.props.title</h3>
    }
} else {
    result = null
}
{result}

2.数据循环渲染

import React from 'react'
export default class ListLoopDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            lists: [
                {name: '张无忌', age: 25},
                {name: '张翠山', age: 35},
                {name: '张三丰', age: 45},
                {name: '张敏敏', age: 25},
            ]
        }
    }

    render(
        return (<div>
            <ul>
                {this.state.lists.map(item => {
                    return <li key={item.name}>
                        <span>{item.name}</span>
                        <span>{item.age}</span>
                    </li>
                })}
            </ul>
        </div>)
    )
}

四.事件监听

以点击事件为例,使用方法如下:

//小驼峰写法,事件名用{}包裹
<button onClick={}></button>

由于react的this指向问题,所以在事件绑定时要特别注意,一般使用的事件绑定写法有三种:

1. 第一种利用bing绑定,写法如下,这种比较少用

//在constructor里面利用bing绑定继承this,解决方法的this指向问题
constructor(props) {
    super(props)
    this.showTitleFun = this.showTitleFun.bind(this)
}
showTitleFun() {
    //执行某些操作
    this.setState({})
}
//在DOM元素上直接使用
<button onClick={this.showTitleFun}>不显示title</button>

2.第二种箭头函数写法

showTitleFun = () => {
    //执行某些操作
    this.setState({})
}
//在DOM元素上直接使用
<button onClick={this.showTitleFun}>不显示title</button>

3.第三种直接使用箭头函数返回一个函数

showTitleFun() {
    //执行某些操作
    this.setState({})
}
<button onClick={() => this.showTitleFun()}>不显示title</button>

五. React样式编写

1.行内样式编写

<img style={{ width: "100px", height: "100px" }} />

2.添加类名,区别html标签类名

<img className="img" />

3.添加属性

<img src={logo} />

六. React实现双向数据绑定

使用input为例的双向数据绑定要点:
1.动态绑定value属性

//在state里面定义一个绑定input的value属性
this.state = {
    inputVal: 'me是input的初始值'
}
//然后在input里动态绑定上面定义的变量
<input type="text" value={this.state.inputVal} </input>

2.监听input的onChange事件

//定义onChange绑定的事件
inputValChange = e => {
    this.setState({
        text: e.target.value
    })
}
//在input上绑定inputValChange到onChange上
<input type="text" value={this.state.inputVal} onChange={e => this.inputValChange(e)} />

七. React生命周期

1. componentWillMount 组件将要挂载
2. componentDidMount 组件已经挂载
3. componentWillReceiveProps 父组件传递的属性有变化,做相应响应
4. shouldComponentUpdate 组件是否需要更新,返回布尔值,true则当前组件更新,false当前组件不更新;优化点
5. componentWillUpdate 组件将要更新
6. componentDidUpdate  组件已经更新
7. componentWillUnmount 组件已经销毁

第三节:React组件写法

一. 傻瓜组件和聪明组件区别

1.傻瓜组件也叫展示组件,负责根据props显示页面信息
2.聪明组件也叫容器组件,负责数据的数据、处理
3.分清楚展示组件和容器组件的优势
① 分离工作组件和展示组件
② 提高组件的重要性
③ 提高组件可用性和代码阅读
④ 便于测试于后续的维护

二.函数式组件

1. 函数式组件是一种无状态组件,是为了创建纯展示组件,这种组件只负责根据传入的props来展示,不涉及到要state状态的操作;
2. 组件不会被实例化,整体渲染性能得到提升;
3. 组件不能访问this对象;
4. 组件无法访问生命周期的方法;
5. 无状态组件只能访问输入的props,同样的props会得到同样的渲染结果,不会有副作用;
6. 官方文档说:在大部分React代码中,大多数组件被写成无状态的组件,通过简单组合可以构建成其他的组件等,这种通过多个简单然后合并成一个大应用的设计模式被提倡;
具体写法如下:

//这种写法是新建一个组件页面把组件暴露出去的写法
import React from 'react'
export default function xxx() {
    return (
        <div>我是函数式组件</div>
    )
}

//这种写法是在页面内部创建组件不用给外部使用,只供页面内部使用
function xxx() {
    return (
        <div>我是函数式组件</div>
    )
}

三.class类组件

1. React.createClass是react刚开始推荐的创建组件的方式,现在基本不会见到了;
2. React.Component是以ES6的形式来创建react的组件的,是React目前极为推荐的创建有状态的组件的方式
在里面我们可以写入我们的状态、生命周期、构造函数等
具体使用如下:

import React, { Component } from 'react'
export default class ConditionLoop extends Component {
    render() {
        return (
            <div></div>
        )
    }
}

 

第四节:React组件化与UI库使用

一.引入ant-design组件库

1.安装ant-design(在项目demo目录下执行 ): 

npm install antd --save

2.使用button组件为例:

import React, { Component } from 'react'
import Button from 'antd/lib/button'
import 'antd/dist/antd.css'
export default class AntDesign extends Component {
    render() {
        return (
            <div>
                <Button type='primary'>按钮</Button>
            </div>
        )
    }
} 

二. 配置ant-design按需加载

官方文档

需要对antd进行配置按需加载,需要对create-react-app的默认配置进行自定义
更改我们的启动插件:
1. 引入react-app-rewired并修改package.json里的启动配置。由于新的react-app-rewired@2.0 版本的关系,还需要安装customize-cra 
2. 安装命令 yarn add react-app-rewired customize-cra (如果还没安装yarn可以先执行npm install yarn -g进行安装)
3. 更改package.json文件

//package.json  - 代表没更改前的代码, + 代表已经更改的代码
"script": {
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",
-   "test": "react-scripts test",
+   "test": "react-app-rewired test",   
}

4. 然后在项目根目录创建一个 config-overrides.js 用于修改默认配置,先不写内容;
5. 执行安装 babel-plugin-import 插件(安装命令: yarn add babel-plugin-import )
6. 修改config-overrides.js文件内容如下:

const { override, fixBabelImports } = require('customize-cra')
module.exports = override(
    fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: 'css',
    }),
);

到这里就成功了(修改文件记得重启项目才生效),可以移除上一节全量引入antd.css的代码了,然后更改引入组件方式

import { Button } from 'antd'

三. PureComponent性能优化

1.PureComponent是内部定制了shouldComponentUpdate生命周期的Component
    ① 它重写了一个方法来替换shouldComponentUpdate生命周期方法
2.平常开发过程中设计组件能使用PureComponent的地方都尽量使用
3.想要使用PureComponent特性要记住两个小原则:
    ① 确保数据类型是值类型
    ② 如果是引用类型,确保地址不变,同时不应当有深层次数据变化
4.使用PureComponent可以省去shouldComponentUpdate生命周期的代码,代码会简单很多
使用实例:

import React, { Component, PureComponent } from 'react'

//class Title extends Component {
class Title extends PureComponent {
    /* shouldComponentUpdate(nextProps) {
        return nextProps.title !== this.props.title
    } */

    render() {
        console.log("我是title组件")
        return (
            <div>
                标题:{this.props.title}
            </div>
        )
    }
}

class Count extends Component {
    render() {
        console.log("我是条数组件")
        return (
            <div>
                条目:{this.props.count}
            </div>
        )
    }
}

export default class Pruememo extends Component {
    constructor(props) {
        super(props)
        this.state = {
            title: '这是标题',
            count: 10,
        }
    }

    componentDidMount() {
        setInterval(() => {
            this.setState({
                count: this.state.count + 1,
            })
        }, 1000);
    }

    render(){
        return (
            <div>
                <hr/>
                <Title title={this.state.title}></Title>
                <Count count={this.state.count}></Count>
            </div>
        )
    }
}

四. React.memo性能优化

1. React.memo是一个高阶组件的写法
2. React.memo让函数组件也拥有了PrueComponent的功能
使用例子如下:

const MemoComponent = React.memo((props) => {
    return (
        <div>
            <p>{props.xxx}</p>
            <p>{props.xxx}</p>
        </div>
    )
});

完整代码如下:

import React, { Component, PureComponent } from 'react'

const Title = React.memo((props) => {
    console.log('我是title组件')
    return (
        <div>
            标题:{props.title}
        </div>
    )
});

class Count extends Component {
    render() {
        console.log("我是条数组件")
        return (
            <div>
                条目:{this.props.count}
            </div>
        )
    }
}

export default class Pruememo extends Component {
    constructor(props) {
        super(props)
        this.state = {
            title: '这是标题',
            count: 10,
        }
    }

    componentDidMount() {
        setInterval(() => {
            this.setState({
                count: this.state.count + 1,
            })
        }, 1000);
    }

    render(){
        return (
            <div>
                <hr/>
                <Title title={this.state.title}></Title>
                <Count count={this.state.count}></Count>
            </div>
        )
    }
}

五. 复合组件写法

组件复合类似于在vue框架里面用的组件插槽
具体使用方式如下:

import React from 'react'

function FuncOne(props) {
    return (
        <div style="{{ border: `4px solid ${props.color || "blue"}` }}">
            //等同于vue中匿名插槽
            {props.children}
            //等同于vue中具名插槽
            <div className="abc">{props.footer}</div>
        </div>
    )
}

export default function FuncTwo() {
    const confirmBtn = {
        <button onClick={() => alert("学习React")}></button>
    };
    return (
        <FuncOne color="green" footer={confirmBtn}>
            <h3>Hello World!</h3>
        </FuncOne>
    )
}

第五节:高阶组件(HOC)

一. 高阶组件

1.高阶组件-- HOC(Higher-Order Components)
2.高阶组件是为了提高组件的服用率而出现的,抽离具有相同逻辑或逻辑或相同展示的组件;
3.高阶组件其实是一个函数,接收一个组件,然后返回一个新的组件,返回的这个新的组件可以对属性进行封装,也可以重写部分生命周期;
使用例子如下:

//创建withLearnReact高阶组件,传递一个组件进去,返回一个新的组件NewComponent
const withLearnReact = (Component) => {
    const NewCompoent = (props) => {
        return <Component {...props} name="张三丰" />
    }
    return NewCompoent
}

完整使用实例:

import React, { Component } from 'react'

//定义一个高阶组件,高阶组件一般以with开头,返回函数组件
const withLearnReact = (Component) => {
    const NewCompoent = (props) => {
        //一定要记得加上接收父组件的解构属性props
        return <Component {...props} name="张翠山"></Component>
    }
    return NewCompoent
}

class HOC extends Component {
    render() {
        return (
            <div>
                <h3>高阶组件(HOC)</h3>
                <p>姓名:{this.props.name}</p>
            </div>
        )
    }
}

export default withLearnReact(HOC)

二.高阶组件的链式调用

使用情况如下:
编写一个高阶组件进行属性的添加;
编写一个高阶组件编写生命周期;
然后将以上两个高阶组件进行链式调用;
使用例子如下:

import React, { Component } from 'react'

//定义一个高阶组件,高阶组件一般以with开头,返回函数组件
const withLearnReact = (Component) => {
    const NewCompoent = (props) => {
        //一定要记得加上接收父组件的解构属性props
        return <Component {...props} name="张翠山"></Component>
    }
    return NewCompoent
}

//定义第二个高阶组件,重写生命周期,注意重写生命周期需要class组件即返回的是class组件
const withLifeCycle = (Comp) => {
    class NewCompoent extends Component {
        //重写组件的生命周期
        componentDidMount(){
            console.log("重写componentDidMount生命周期")
        }
        render() {
            return <Comp {...this.props}></Comp>
        }
    }
    return NewCompoent
} 

class HOC extends Component {
    render() {
        return (
            <div>
                <h3>高阶组件链式调用</h3>
                <p>姓名:{this.props.name}</p>
            </div>
        )
    }
}

export default withLifeCycle(withLearnReact(HOC))

三.高阶组件装饰器写法

由于高阶组件链式调用的写法看起来比较的麻烦也不好理解。逻辑会看的比较绕
ES7中就出现了装饰器的语法,专门拿来处理这种问题(类似于Java的注解)
安装支持装饰器语法的babel编译插件:

npm install -D @babel/plugin-proposal-decorators

更改配置文件config-overrides.js代码(修改后重启项目):

const { override, fixBabelImports, addDecoratorsLegacy  } = require('customize-cra')
module.exports = override(
    //配置antd按需加载
    fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: 'css',
    }),
 
    //配置支持高阶组件装饰器写法
    /*addBabelPlugins(`
        [
            '@babel/plugin-proposal-derorators',
            {legacy: true}
        ],
    ),*/
    addDecoratorsLegacy()
);

组件完整代码如下:

import React, { Component } from 'react'

//定义一个高阶组件,高阶组件一般以with开头,返回函数组件
const withLearnReact = (Component) => {
    const NewCompoent = (props) => {
        //一定要记得加上接收父组件的解构属性props
        return <Component {...props} name="张翠山"></Component>
    }
    return NewCompoent
}

//定义第二个高阶组件,重写生命周期,注意重写生命周期需要class组件即返回的是class组件
const withLifeCycle = (Comp) => {
    class NewCompoent extends Component {
        //重写组件的生命周期
        componentDidMount(){
            console.log("重写componentDidMount生命周期")
        }
        render() {
            return <Comp {...this.props}></Comp>
        }
    }
    return NewCompoent
} 

@withLearnReact
@withLifeCycle
class HOC extends Component {
    render() {
        return (
            <div>
                <h3>高阶组件链式调用</h3>
                <p>姓名:{this.props.name}</p>
            </div>
        )
    }
}

export default HOC

四. 组件上下文(context)通信

1.上下文context有两个角色:
① Provider 数据提供
② Consumer 数据读取
2.使用context可以避免通过中间元素传递props,context的设计目的是为了共享对于一个组件树而言是"全局"的数据
不使用context的情况代码如下:

import React, { Component } from 'react'
let store = {
    name = "张无忌",
    age = 25,
}

class Info extends Component {
    render() {
        return (
            <div>
                <p>姓名:{this.props.name}</p>
                <p>年龄:{this.props.age}</p>
            </div>
        )
    }
}

function ToolBar(props) {
    return (
        <div>
            <Info {...props}></Info>
        </div>
    )
}

export default class Context1 extends Component {
    render() {
        return (
            <ToolBar name={store.name} from={store.age}></ToolBar>
        )
    }
}

使用context,避免中间props元素的传递写法,代码如下:

import React, { Component } from 'react'
//1.创建上下文
const XdContext = React.createContext()
const {Provider, Consumer} = XdContext
//创建一个传递的数据源
let store = {
    name: '张无忌',
    age: 25,
}

class Info extends Component {
    render() {
        return (
            <Consumer>
                {
                    store => {
                        return (
                            <div>
                                <p>姓名:{store.name}</p>
                                <p>年龄:{store.age}</p>
                            </div>
                        )
                    }
                }
            </Consumer>
        )
    }
}

function ToolBar(props) {
    return (
        <div>
            <Info></Info>
        </div>
    )
}

export default class Context1 extends Component {
    render(){
        return (
            <Provider value={store}>
                <ToolBar></ToolBar>
            </Provider>
        )
    }
}

第六节:React函数式编程之HOOK

一. React Hooks

1.Hook是React 16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。
2.在进行之前,记住以下:
    ①完全可选的。你无需重写任何已有代码就可以在一些组件中尝试Hook。但是如果你不想,你不必现在就去学习或使用Hook。
    ②100%向后兼容的。Hook不包含任何破坏性改动。
    ③现在可用。Hook已发布于v16.8.0
3.没有计划从React中移除class。
4.Hook不会影响你对React概念的理解。恰恰相反,Hook为已知的React概念提供了更直接的API: props,state, context, refs以及生命周期。接下来的学习我们会发现,Hook还提供了一种更强大的方式来组合他们。
5.React Hooks解决了什么问题:
    ①函数组件不能使用state,一般只用于一些简单无交互的组件,用作信息展示,即我们上面说的傻瓜组件使用,如果需要交互更改状态等复杂逻辑时就需要使用class组件了。React Hooks让我们更好的拥抱函数式编程,让函数式组件也能使用state功能,因为函数式组件比class组件更简洁好用,因为React Hooks的出现,相信未来我们会更多的使用函数式组件。
    ②副作用问题:
        我们一般称数据获取、订阅、定时执行任务、手动更改ReactDOM这些行为都可以成为副作用;
        由于React Hooks的出现,我们可以使用useEffect来处理组件副作用问题,所以我们的函数式组件也能进行副作用逻辑的处理了;
    ③有状态的逻辑重用组件
    ④复杂的状态管理:
        之前我们使用redux、dva、mobx第三方状态管理器来进行复杂的状态管理;
        现在我们可以使用useReducer、useContext配合使用实现复杂状态管理,不用再依赖第三方状态管理器
    ⑤开发效率和质量问题:
        函数式组件比class组件简洁,开发的体验更好,效率更高同时应用的性能也更好;

封装好的React Hooks,可以来这里学习封装自定义Hooks

整理了N个实用案例帮你快速迁移到React Hooks(收藏慢慢看系列)    学习博客(强烈推荐)

二. useState特性

官方文档

1.useState -- 组件状态管理钩子,能够使函数组件使用state
2.基本使用如下:

//state是要设置的状态
//setState是更新state的方法,只是一个方法名,可以随意更改
//initState是初始的state,可以随意的数据类型,也可以是回调函数,但是函数必须是有返回值
const [state, setState] = useState(initState)

完整使用例子代码如下:

import React, {useState} from 'react'

export default function  HookUseState(){
    const [count, setCount] = useState(0)
    return (
        <div>
            <div>你点击了{count}次</div>
            <button onClick={() => setCount(count+1)}>点击</button>
        </div>
    )
}

三. useEffect特性

官方文档

1.useEffect -- 副作用处理钩子
    ①数据获取、订阅、定时执行任务、手动修改ReactDOM这些行为都可以称为副作用。而useEffect就是为了处理这些副作用而生的;
    ②useEffect也是componentDidMount、componentDidUpdate和componentWillUnmount这几个生命周期方法的统一;
2.useEffect的基本使用如下:
useEffect(callback, array)
    ①callback可以返回一个函数,用作清理:

useEffect(() => {
    //副作用逻辑
    xxxx
    return () => {
        //清理副作用需要清理的内容
        //类似于componentWillUnmount,组件渲染和组件卸载前执行的代码
    }
}, [])

   ② array(可选参数):数组,用于控制useEffect的执行,分三种情况:
        空数组,则只会执行一次(即初次渲染render),相当于componentDidMount;
        非空数组,useEffect会在数组发生后执行;
        不填array这个数组,useEffect每次渲染都会执行;

完整使用例子代码如下:

import React, {useState, useEffect} from 'react'

export default function HookUseEffect() {
    const [count, setCount] = useState(1)

    useEffect(() => {
        //更新页面标题
        document.title = `您点击了${count}次了`
        return () => {
            console.log("组件卸载或更新了")
        }
    }, [count])

    return(
        <div>
            <div>你点击了{count}次</div>
            <button onClick={() => setCount(count+1)}>点击</button>
        </div>
    )
}

四.useContext特性

1.context就是用来更方便的实现全局数据共享的,但是由于它并不是那么好用,所以我们一般会使用第三方状态管理器来实现全局数据共享
    ① redux
    ② dva
    ③ mobx
2.useContext(context)是针对context上下文提出的一个Hooks提出的一个API,它接受React.createContext()的返回值作为参数,即context对象,并返回最近的context
3.使用useContext是不需要再使用Provider和Consumer的
4.当最近的context更新时,那么使用该context的hook将会重新渲染
基本使用如下:

import React, {useContext} from 'react'
const Context = React.createContext({age: '25', name: '张无忌'})
const ageComp = () => {
    //使用useContext
    const ctx = useContext(Context)
    return (
        <div>姓名:{ctx.name}</div>
        <div>年龄:{ctx.age}岁</div>
    )
}

使用实例一代码如下:
HookUseContext.js文件代码:

import React, {useContext} from 'react'
import HookUseContextChild from './HookUseContextChild'
const Context = React.createContext({name: '张无忌', age: 25})
export default function HookUseContext(){
    const ctx = useContext(Context)
    return (
        <div>
            <hr></hr>
            <h3>使用UseContext</h3>
            <p>姓名:{ctx.name}</p>
            <p>年龄:{ctx.age}</p>
            //需要给每一个引入的子组件传递Context
            <HookUseContextChild Context={Context}></HookUseContextChild>
        </div>
    )
}

HookUseContextChild.js文件代码:

import React, {useContext} from 'react'
import {Context} from './HookUseContext'
export default function HookUseContextChild({Context}) {
    const ctx = useContext(Context)
    return (
        <div>
            <h3>子组件接收父组件传递的context</h3>
            <p>姓名:{ctx.name}</p>
            <p>年龄:{ctx.age}</p>
        </div>
    )
}

实例一需要给每一个引入的子组件传递Context,所以一般项目是将数据单独放到数据文件中,其他组件引入解构使用,实例二代码如下:
数据文件store.js代码:

import React from 'react'
export const tom = React.createContext({name: 'tom', age: 25})
export const jerry = React.createContext({name: 'jerry', age: 28})

HookUseContextOne.js文件代码:

import React, {useContext} from 'react'
import {tom} from './store'
export default function HookUseContextOne(){
    const ctx = useContext(tom)
    return (
        <div>
            <p>姓名:{ctx.name}</p>
            <p>年量:{ctx.age}</p>
        </div>
    )
}

HookUseContextTwo.js文件代码:

import React, {useContext} from 'react'
import {jerry} from './store'
export default function HookUseContextTwo()
{
    const ctx = useContext(jerry)
    return (
        <div>
            <p>姓名:{ctx.name}</p>
            <p>年龄:{ctx.age}</p>
        </div>
    )
}

五.useReducer特性

1.useReducer是useState的一个增强体,可以用于处理复杂的状态管理;
2.useReducer可以完全替代useState,只是我们简单的状态管理用useState比较易用,useReducer的设计灵感源自于redux的reducer
3.对比一下useState和useReducer的使用:

//useState的使用方法
const [state, setState] = useState(initState)

//useReducer的使用方法
const [state, dispatch] = useReducer(reducer, initState, initAction)

4.useReducer的参数介绍:
    ① reducer是一个函数,根据action状态处理并更新state
    ② initState初始化的state
    ③ initAction是useReducer初次执行时被处理的action
5.返回值state,dispath介绍:
    ① state状态值
    ② dispatch是更新state的方法,它接受action作为参数
6.useReducer只需要调用dispatch(action)方法传入action即可更新state,使用如下:

//dispatch是用来更新state的,当dispatch被调用的时候,reducer方法也会被调用,同时根据action的传入内容去更新state, action是传入一个描述操作的对象
dispatch({type: 'add'})

7.reducer是redux的产物,他是一个函数,主要用于处理action,然后返回最新的state,可以把reducer理解成是action和state的转换器,它会根据action的描述去更新state,使用例子:

(state, action) => Newstate

具体使用代码例子:

import React, {useReducer} from 'react'
const initState = {count: 0} //state的初始值
const reducer = (state, action) => {
    switch(action.type) {
        //当type是reset时,重置state的值回到初始化时候的值
        case 'reset':
            return initState
        //当type的值是add时,让count+1
        case 'add':
            return {count: state.count+1}
        //当type的值是reduce是,让count-1
        case 'reduce':
            return {count: state.count-1}
        //当type不属于上面任意一个值,state不做更改,直接返回当前state
        default:
            return state
    }
}
export default function HookUseReducer()
{
    const [state, dispatch] = useReducer(reducer, initState)
    return (
        <div>
            <p>当前数量是: {state.count}</p>
            <p><button onClick={() => dispatch({type: 'reset'})}>重置</button></p>
            <p><button onClick={() => dispatch({type: 'add'})}>加一</button></p>
            <p><button onClick={() => dispatch({type: 'reduce'})}>减一</button></p>
        </div>
    )
}

六.useMemo特性

useMemo用于性能优化,通过记忆值来避免在每个渲染上执行高开销的计算
适用于发展的计算场景,例如复杂的列表渲染,对象深拷贝等场景;
使用方法如下:
const memoValue = useMemo(callback, array)
① callback是一个函数用于处理逻辑
② array控制useMemo重新执行的数组,array改变时才会重新执行useMemo
③ useMemo的返回值是一个记忆值,是callback的返回值

使用方法如下:

const obj1 = {name: '张无忌', age: 25}
const obj2 = {name: '张敏敏', age: 24}
const memoValue = useMemo(() => Object.assign(obj1, obj2), [obj1, obj2])
//使用方式
<div>姓名:{memoValue.name} --- 年龄:{memoValue.age}</div>

不能在useMemo里面写副作用逻辑处理,副作用的逻辑处理放在useEffect内进行处理。

七.useCallback特性

useCallback特性和useMemo一样,也是用于性能优化的
基本使用方法:
const memoCallback = useCallback(callback, array)
① callback是一个函数用于逻辑处理;
② array控制useCallback重新执行的数组,array改变时才会重新执行useCallback;
③ 根useMemo不一样的是返回值是callback本身,而useMemo返回的是callback函数的返回值;
使用方法如下:

const obj1 = {name: '张无忌', age: 25}
const obj2 = {name: '张敏敏', age: 24}
const memoCallback = useCallback(() => Object.assign(obj1, obj2), [obj1, obj2])
//使用方式
<div>姓名:{memoCallback().name} --- 年龄:{memoCallback().age}</div>

八.useRef特性

方便我们访问操作dom
使用方法如下:

import React, {useRef} from 'react'
const UseRefComp = () => {
    //创建ref
    const inputRef = useRef
    const getValue = () => {
        console.log(inputRef.current.value)
    }
    //挂载
    return (
        <div>
            <input ref={inputRef} type='text' />
            <button onClick={getValue}>获取input值</button>
        </div>
    )
}

九.自定义Hooks

Hooks其实就是一个封装好的钩子函数供我们调用
只是我们自己封装的时候要特别注重性能,重复渲染这些问题,官方封装的就比较完美;
简单封装一个改变页面标题的自定义Hooks:

import React, {useEffect} from 'react'
//封装的Hooks用use开头
const useChangeTitle = (title) => {
    useEffect(() => {
        document.title = title
    }, [title])
}
export default (props) => {
    useChangeTitle('自定义修改标题Hooks')
    return(
        <div>
            测试自定义Hooks
        </div>
    )
}

十.总结React Hooks使用规则

1.只在顶层调用Hooks
    ① Hooks的调用尽量只在顶层作用域进行调用;
    ② 不要在循环,条件或者是嵌套函数中调用Hook,否则可能会无法确保每次组件渲染时都以相同的顺序调用Hook
2.只在函数组件调用Hooks
    ① React Hooks目前只支持函数组件,所以别在class组件或者普通的函数里面调用Hook钩子函数;
3.React Hooks的应用场景如下:
    ① 函数组件
    ② 自定义组件
4.在未来的版本React Hooks会扩展到class组件,但是现阶段不能在class里使用

第七节:状态管理器Redux

一.Redux成员及其数据流

1.actions
    ① actions其实是描述操作的对象,我们调用dispatch时需要传入次对象;
2.store
    ① store是整个应用的数据存储仓库,把我们全局管理的状态数据存储起来;
3.reducers
    ① reducers接收action并更新store
4.注意:redux是一个单独的数据流框架,根react并没有直接的联系,我们也可以在其他复杂项目里使用redux进行数据管理,当我们不知道是否应该使用redux的时候,我们都是不需要的,因为只有我们很肯定redux能帮助我们管理好复杂项目数据流的时候他才能发挥它的威力,简单的项目我们只需要state+props+context就够了

二.redux编写累加器

安装redux

npm install redux --save

编写使用redux的步骤:
1.从redux引入createStore用来创建数据仓库store
    ① createStore是一个函数,需要传入reducer作为参数,返回值是我们需要的store;
2.在使用页面引入数据仓库store
    ① 通过getState()办法可以获取到数据仓库里的状态数据state
    ② 通过dispatch(action)可以触发更改reducer函数
    ③ 每次触发dispatch都会触发store.subscribe()方法,用来从新触发页面渲染
代码展示:
项目index.js文件代码:

import './index.css';
import App from './App';
import store from './Redux/store';

const render=() => ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
render()
store.subscribe(render)

store.js文件代码:

import {createStore} from 'redux'
const firstReducer = (state=0, action) => {
    switch(action.type) {
        //当传入action的type为add的时候给state+1
        case 'add':
            return state+1
        //当传入action的type为reduce的时候给state-1
        case 'reduce':
            return state-1
        default:
            return state
    }
}
//创建数据仓库,把我们编写的reducer作为参数传入createStore
const store = createStore(firstReducer)
export default store

FirstRedux.js文件代码:

import React, {Component} from 'react'
import store from './store'
export default class FirstRedux extends Component
{
    render(){
        return (
            <div>
                <hr />
                <h2>尝试使用redux编写一个累加器</h2>
                //通过getState方法获取数据仓库里面的状态数据state
                {store.getState()}
                <div>
                    <button onClick={() => store.dispatch({type: 'add'})}>加一</button>
                    <button onClick={() => store.dispatch({type: 'reduce'})}>减一</button>
                </div>
            </div>
        )
    }
}

三.react-redux进行改造累加器

1.由于redux的写法太繁琐,还每次都需要重新调用render,不太复合我们了解react编程;
2.react-redux出境,安装react-redux:

npm install react-redux --save

3.React-redux提供两个api供我们使用:
    ① Provider 顶级组件,功能为给我们提供数据;
    ② connect 高阶组件,功能为提供数据和方法;
4.以下为使用react-redux改造累加器的代码,只需留意index.js和FirstRedux.js,store.js暂时不用做改变
代码展示:
FirstRedux.js文件代码:

import React, {Component} from 'react'
import {connect} from 'react-redux'
//写一个返回数据的方法,供connect使用,connect会帮我们把数据转成props
const mapStateToProps = (state) => {
    return {
        count: state
    }
}

//写一个返回dispatch方法的方法供connect使用,connect帮我们把dispatch转成props
const mapDispatchToProps = dispatch => {
    return {
        add: () => dispatch({type: 'add'}),
        reduce: () => dispatch({type: 'reduce'})
    }
}
class FirstRedux extends Component {
    render() {
        return (
            <div>
                <h2>使用redux编写累加器</h2>
                {this.props.count}
                <div>
                    <button onClick={() => this.props.add()}>加一</button>
                    <button onClick={() => this.props.reduce()}>减一</button>
                </div>
            </div>
        )
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(FirstRedux)

项目index.js文件代码:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import store from './Redux/store';
import {Provider} from 'react-redux';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

四.高阶组件使用装饰器模式

connect高阶组件用装饰器模式会使我们的代码看起来更简洁易懂;
使用装饰器进行我们的代码优化:
FirstRedux.js文件代码:

import React, {Component} from 'react'
import {connect} from 'react-redux'
@connect(
    state => ({count:state}),
    dispatch => ({
        add:() => dispatch({type: 'add'}),
        reduce:() => dispatch({type: 'reduce'})
    })
)
class FirstRedux extends Component {
    render() {
        return (
            <div>
                <h2>使用装饰器优化累加器</h2>
                {this.props.count}
                <div>
                    <button onClick={() => this.props.add()}>加一</button>
                    <button onClick={() => this.props.reduce()}>减一</button>
                </div>
            </div>
        )
    }
}
export default FirstRedux

五.中间件redux-thunk于redux-logger

由于redux reducer默认只支持同步,实现异步任务或者延时任务时,我们就要借助中间件的支持了;
1.安装redux-thunk,支持我们reducer在异步操作结束后自动执行

npm install redux-thunk --save

2.安装redux-logger,打印日志记录协助本地测试

npm install redux-logger --save

使用代码如下:
store.js文件代码:

import {createStore, applyMiddleware} from 'redux'
import logger from 'redux-logger'
import thunk from 'redux-thunk'
const firstReducer = (state=0, action) => {
    console.log(action)
    switch(action.type) {
        //当传入action的type为add的时候给state+1
        case 'add':
            return state + 1
        //当传入actio的type为reduce的时候给state-1
        case 'reduce':
            return state - 1
        default:
            return state
    }
}
//创建数据仓库,把我们编写的reducer作为参数传入createStore
//有一个注意点就是logger最好放在最后,日志最后输出才不会出bug,因为中间件时按顺序执行
const store = createStore(firstReducer, applyMiddleware(thunk, logger))
export default store

FirstRedux.js文件代码:

import React, {Component} from 'react'
import {connect} from 'react-redux'
@connect(
    state => ({count:state}),
    dispatch => ({
        add:() => dispatch({type: 'add'}),
        reduce:() => dispatch({type: 'reduce'}),
        addSync:() => setTimeout(() => {
            //延时2s后cout加一
            dispatch({type: 'add'})
        }, 2000),
    })
)
class FirstRedux extends Component {
    render() {
        return (
            <div>
                <h2>使用装饰器优化累加器</h2>
                {this.props.count}
                <div>
                    <button onClick={() => this.props.add()}>加一</button>
                    <button onClick={() => this.props.reduce()}>减一</button>
                    <button onClick={() => this.props.addSync()}>延时加一</button>
                </div>
            </div>
        )
    }
}
export default FirstRedux

六.抽离reducer和action进行统一管理

第一步新建一个count.redux.js存放我们的reducer和action
count.redux.js文件代码:

//把reducer和action抽离出来再同一个文件下进行维护
const firstReducer = (state=0, action) => {
    console.log(action)
    switch(action.type) {
        case 'add':
            return state + 1
        case 'reduce':
            return state - 1
        default:
            return state
    }
}
const add = () => ({type: 'add'})
const reduce = () => ({type: 'reduce'})
const addSync = () => dispatch => {
    setTimeout(() => {
        dispatch({type: 'add'})
    }, 2000)
}
export {firstReducer, add, reduce, addSync}

FirstRedux.js文件代码:

import React, {Component} from 'react'
import {connect} from 'react-redux'
import {add, reduce, addSync} from './count.redux'
@connect(
    state => ({count:state}),
    {add, reduce, addSync}
    /* dispatch => ({
        add:() => dispatch({type: 'add'}),
        reduce:() => dispatch({type: 'reduce'}),
        addSync:() => setTimeout(() => {
            //延时2s后cout加一
            dispatch({type: 'add'})
        }, 2000),
    }) */
)
class FirstRedux extends Component {
    render() {
        return (
            <div>
                <h2>抽离reducer和action进行统一管理</h2>
                {this.props.count}
                <div>
                    <button onClick={() => this.props.add()}>加一</button>
                    <button onClick={() => this.props.reduce()}>减一</button>
                    <button onClick={() => this.props.addSync()}>延时加一</button>
                </div>
            </div>
        )
    }
}
export default FirstRedux

项目index.js文件代码:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux'
import logger from 'redux-logger'
import thunk from 'redux-thunk'
import {firstReducer} from './Redux/count.redux';

const store = createStore(firstReducer, applyMiddleware(thunk, logger))
ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值