React学习笔记——扩展内容

1. setState

使用setState更新状态有 2 种写法。

对象式函数式的简写方式(语法糖)。

使用原则(非必须):

  • 新状态不依赖于原状态 ===> 使用对象式
  • 新状态依赖于原状态 ===> 使用函数式
  • 如果需要在setState()执行后获取最新的状态数据,要在callback函数中读取

1. 对象式

setState(stateChange, [callback]);
  • stateChange为状态改变对象(该对象可以体现出状态的更改)
  • callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
  • 状态的更新是异步的,如果想要查看更新后的状态,需要写在callback
const { count } = this.state;
this.setState({ count: count + 1 }, () => {
    console.log(this.state.count);
});

2. 函数式

setState(updater, [callback]);
  • updater为返回stateChange对象的函数,可以接收到stateprops

  • callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用

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

2. 路由组件的lazyLoad

通过 React 的lazy函数配合import()函数动态加载路由组件,使路由组件代码分开打包。

import Loading from './Loading';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));

通过<Suspense>标签指定在加载得到路由打包文件前显示一个自定义loading界面

<Suspense fallback={<Loading />}>
    {/* 注册路由 */}
    <Route path="/about" component={About} />
    <Route path="/home" component={Home} />
</Suspense>

3. Hooks

Hook是 React 16.8.0 版本增加的新特性,可以在函数组件中使用state以及其他的 React 特性。下面介绍三个常用的Hook

  • State Hook:React.useState()
  • Effect Hook:React.useEffect()
  • Ref Hook:React.useRef()

1. State Hook

State Hook 让函数组件也可以有state状态,并进行状态数据的读写操作。

const [xxx, setXxx] = React.useState(initValue); // 解构赋值
  • useState()

    参数:第一次初始化指定的值在内部作缓存

    返回值:包含 2 个元素的数组,第 1 个为内部当前状态值,第 2 个为更新状态值的函数

  • setXxx()2 种写法

    setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值

    setXxx(value => newValue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

function Demo() {
    const [count, setCount] = React.useState(0);

    //加的回调
    function add() {
        // 第一种写法
        // setCount(count + 1);
        // 第二种写法
        setCount(count => count + 1);
    }

    return (
        <div>
            <h2>当前求和为:{count}</h2>
            <button onClick={add}>点我+1</button>
        </div>
    );
}

2. Effect Hook

Effect Hook 可以在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)。

React 中的副作用操作

  • ajax请求数据获取
  • 设置订阅 / 启动定时器
  • 手动更改真实 DOM
useEffect(() => {
    // 在此可以执行任何带副作用操作
    // 相当于componentDidMount()
    return () => {
        // 在组件卸载前执行
        // 在此做一些收尾工作, 比如清除定时器/取消订阅等
        // 相当于componentWillUnmount()
    };
}, [stateValue]); // 监听stateValue
// 如果省略数组,则检测所有的状态,状态有更新就又调用一次回调函数
// 如果指定的是[], 回调函数只会在第一次render()后执行

可以把useEffect()看做如下三个函数的组合:

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()
function Demo() {
    const [count, setCount] = React.useState(0);

    React.useEffect(() => {
        let timer = setInterval(() => {
            setCount(count => count + 1);
        }, 500);
        console.log('@@@@');
        return () => {
            clearInterval(timer);
        };
    }, [count]);
    // 检测count的变化,每次变化,都会输出'@@@@'

    //加的回调
    function add() {
        // 第一种写法
        // setCount(count + 1);
        // 第二种写法
        setCount(count => count + 1);
    }

    // 卸载组件的回调;
    function unmount() {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'));
    }

    return (
        <div>
            <h2>当前求和为:{count}</h2>
            <button onClick={add}>点我+1</button>
            <button onClick={unmount}>卸载组件</button>
        </div>
    );
}

3. Ref Hook

Ref Hook 可以在函数组件中存储/查找组件内的标签或任意其它数据。

保存标签对象,功能与React.createRef()一样

const refContainer = useRef();
function Demo() {
    const myRef = React.useRef();

    //提示输入的回调
    function show() {
        alert(myRef.current.value);
    }

    return (
        <div>
            <input type="text" ref={myRef} />
        </div>
    );
}

4. Fragment

使用<Fragment><Fragment>后,可以不用必须有一个真实的 DOM 根标签了。

import React, { Component, Fragment } from 'react';

export default class Demo extends Component {
    render() {
        return (
            <Fragment key={1}>
                <input type="text" />
                <input type="text" />
            </Fragment>
        );
    }
}

使用空标签<></>包裹也可以,他们的区别如下:

  • <Fragment><Fragment>:可以接收key属性,不能接收其他属性
  • <></>:不能接受属性

5. Context

一种组件间通信方式,常用于祖组件后代组件间通信。

在组件外部创建Context容器对象:

const XxxContext = React.createContext();

渲染子组时,外面包裹xxxContext.Provider,通过value属性给后代组件传递数据:

<XxxContext.Provider value={数据}>
	子组件
</XxxContext.Provider>

后代组件读取数据:

方式(1),仅适用于类组件

static contextType = xxxContext  // 声明接收context
console.log(this.context); // 读取context中的value数据

方式(2),函数组件类组件都可以:

<XxxContext.Consumer>
	{value => `${value.username},年龄是${value.age}`}
</XxxContext.Consumer>

6. 组件优化

React 中Component组件的2个问题:

  1. 只要执行setState(),即使不改变状态数据,组件也会重新render()
  2. 只要当前组件重新render,就会自动重新render子组件,即使子组件没有发生任何变化,这导致页面更新的效率低下

效率高的做法:

  • 只有当组件的stateprops数据发生改变时才重新render()

问题的原因:

  • Component中的shouldComponentUpdate()总是返回true

解决办法:

  • 重写shouldComponentUpdate()方法

    比较新旧stateprops数据,如果有变化才返回true,否则返回false

  • 使用PureComponent组件代替Component组件

    PureComponent重写了shouldComponentUpdate(),只有stateprops数据有变化才返回true

    只是进行stateprops数据的浅比较,如果只是数据对象内部数据变了,返回false

    所以不要直接修改state数据,而是要产生新数据

7. render props

向组件内部动态传入带内容的结构(标签)。

1. children props

<A>
    <B>xxxx</B>
</A>

B组件通过this.props.children获取便签中的数据。

但是B组件获得不到A组件内的数据。

2. render props

export default class Parent extends Component {
    render() {
        return (
            <div className="parent">
                <h3>我是Parent组件</h3>
                <A render={name => <B name={name} />} />
            </div>
        );
    }
}

class A extends Component {
    state = { name: 'tom' };
    render() {
        console.log(this.props);
        const { name } = this.state;
        return (
            <div className="a">
                <h3>我是A组件</h3>
                {this.props.render(name)}
            </div>
        );
    }
}

class B extends Component {
    render() {
        console.log('B--render');
        return (
            <div className="b">
                <h3>我是B组件,{this.props.name}</h3>
            </div>
        );
    }
}

相当于A组件内部写了个插槽,可以在Parent组件中任意更改向插槽中插入的组件,并传递A组件的数据。

8. 错误边界

用来捕获后代组件错误,渲染出备用页面。

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。

getDerivedStateFromError + componentDidCatch

export default class Parent extends Component {
    state = {
        hasError: '', // 用于标识子组件是否产生错误
    };

    //当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
    static getDerivedStateFromError(error) {
        console.log('@@@', error);
        // 返回状态对象
        return { hasError: error };
    }

    componentDidCatch(error, info) {
        // 统计页面的错误。发送请求发送到后台去
        console.log(error, info);
    }

    render() {
        return (
            <div>
                <h2>我是Parent组件</h2>
                {this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child />}
            </div>
        );
    }
}

9. 组件间通信方式总结

通信方式:

  1. props

    父子间通过props

    children props

    render props

  2. 消息订阅-发布

    pubs-sub

  3. 集中式管理

    redux

  4. conText

    生产者-消费者模式

组件之间的关系:

  1. 父子组件 —— props
  2. 兄弟组件(非嵌套组件) —— 消息订阅-发布、集中式管理
  3. 祖孙组件(跨级组件) —— 消息订阅-发布、集中式管理、conText

欢迎在我的博客上访问:
https://lzxjack.top/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

火星飞鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值