在我的另一篇文章react入门到入魔中,只用到了setState最简单的用法
this.setState({xxx:'新值'});
由于setState是一个异步更新方法,所以以下写法是拿不到state更新之后的值的
this.setState({xxx:'新值'});
console.log(this.state)
实际上,完整的用法如下
this.setState({xxx:'新值'}, () => {更新之后的回调函数});
函数式的setState接收到两个参数,state和props,函数的返回值将作为最新的state状态
this.setState(
(state, props) => {
return {
xxx: "新值",
};
},
() => {
console.log("更新之后的回调");
}
);
组件懒加载一般指路由组建的懒加载,指在未渲染到该路由组件时,不应该提前渲染到页面上。使用方法极其简单。
// 首先修改路由组件的引入方式
// 修改前
// 现在引入容器组件
// import Counter from "./container/Count/index";
// import Result from "./container/Result/index";
// import User from "./container/User/index";
// 修改之后
import { lazy, Suspense } from "react";
const Counter = lazy(() => import("./container/Count/index"));
const Result = lazy(() => import("./container/Result/index"));
const User = lazy(() => import("./container/User/index"));
import可以作为一个方法引入(ES6标准,按需引入),同时,因为是按需引入,那么就要考虑引入失败的情况,所以要对组件提供一个容错组件。
将路由组件用suspense组件包裹起来
<Suspense fallback={<h1>计算器页面正在加载中</h1>}>
<Counter></Counter>
<Result></Result>
</Suspense>
<Suspense fallback={<h1>用户页面正在加载中</h1>}>
<User></User>
</Suspense>
react16.8之后新增的特性,可以让函数式组件使用state及react特性
几个常见的Hooks
可以让函数式组件使用state
import { useState } from "react";
function HooksDemo() {
const [myName, updateName] = useState("wxs");
const changeName = () => {
updateName('我的新名字')
};
return (
<div>
一个简单的Hooks测试 看看我的名字的值{myName}
<button onClick={changeName}>点击我改一个名字</button>
</div>
);
}
export default HooksDemo;
其中updateName还可以接受一个函数,当更新之后的值需要以原来的值为基础进行更改时使用
updateName((oldValue) => (oldValue + 1))
可以让函数式组件使用生命周期,useEffect函数接受两个参数,第一个参数是一个回调函数,在组件挂载完毕之后调用。第二个参数是一个数组,在数组中的state对象发生改变时也会调用第一个回调函数
const [myName, updateName] = useState("wxs");
const changeName = () => {
updateName('我的新名字')
};
useEffect(() => {
console.log("组件挂载成功或者myName变化了")
},[myName])
Tips: effctHook第一个回调函数返回一个函数将作为componentWillUnMount的回调
在函数式组件中使用ref
const myButton = useRef()
<button onClick={changeName} ref={myButton}>点击我改一个名字</button>
用于跨层级组件间通信。这里以三层嵌套结构为
代码结构如下
如果想从A传递数据到C,用redux显得小题大作,用props又显得太过麻烦。这时可以用到context
1、新建一个context变量 —> 2、 provider包裹父组件提供值
// 因为待会取值的时候要用到同一个context对象,所以在这里通过export暴露出去
export const MyContext = createContext();
// provider必须要用value属性传递值,不然会报错
<MyContext.Provider value='我传递一个值看看'>
<ComponentB></ComponentB>
</MyContext.Provider>
1、第一种方法(仅类式组件能使用)
在类式组件中定义私有变量contextType
// 导入context对象
export const MyContext = createContext();
// 注意变量名一定要是coontextType,不然无法传值
static contextType = MyContext;
如此操作之后就可以通过类组件的this.context取值了
console.log(this.context);
2、第二种方法
同样的需要先导入context对象,然后在对象的consumer标签中可以自由使用传递的值
import { MyContext } from "../ComponentA/ComponentA";
<MyContext.Consumer>
{ value => `${value}` }
</MyContext.Consumer>
目前我们所写的所有组件都是继承于react的component。
对于一般的component存下如下的问题,只要执行setState就会执行组件的render函数。
但是对此会带来严重的效率问题。图中只涉及到了一个子组件,万一一个组件内有2-30个子组件,当父组件设置setState({})并没有改变任何值但缺引发了十几个子组件的render函数。
原因是 component生命周期shoulComponentUpdate总是返回ture。
对此,我们可以重写shouldComponentsUpdate的判断逻辑。在没有属性更新的时候返回false,不触发组件更新。而对此我们要写判断两个对象是否相等的代码逻辑。像这样
const currentState = this.state;
const currentStateKeys = Object.keys(currentState);
const nextStateKeys = Object.keys(nextState);
if (nextStateKeys.length !== currentStateKeys.length) {
// 如果前state和后state对象含有键长度不一样,那么可以肯定state发生了改变
return true;
} else {
let shouldUpdate = false
// 键数组长度一样
currentStateKeys.forEach((currentKey) => {
if (!nextStateKeys.includes(currentKey)) {
// 如果新state有原state不存在的key值,那么可以说明state发生了改变
shouldUpdate = true;
}
});
// 前后state键长度一样,且键名完全一致
currentStateKeys.forEach((currentKey) => {
if (currentState[currentKey] !== nextState[currentKey]) {
// 同键名,值不同 state发生了改变
shouldUpdate = true;
}
});
return shouldUpdate;
}
或者我们可以选择另一种方法。
将component换成pureComponent
错误边界:对子组件的容错处理,看,在A组件中嵌套一个B组件
b组件结构如下
import React, { Component } from "react";
export default class ComponentB extends Component {
state = {
name: "wxs",
studentList: [
{
id: 1,
name: "wxs1",
},
{
id: 2,
name: "wxs2",
},
],
};
render() {
return (
<div>
我是一个B组件
<div>
{this.state.studentList.map((student) => (
<p key={student.id}>{student.name}</p>
))}
</div>
</div>
);
}
}
很正常的显示在页面上
但如果因为某些原因,导致studentLIst不是一个数组,而是其他类型的变量比如字符串
studentList:'aaa'
而导致页面无法正常渲染时,可以通过其父组件的边界容错处理错误信息以及异常情况。
以上为例。
export default class ComponentA extends PureComponent {
static getDerivedStateFromError(err) {
return {
componentError: {
hasError: true,
errorMsg: err,
},
};
}
render() {
return (
<div>
我是一个A组件
{ this.state?.componentError?.hasError ? <div>肯定是b组件出错了</div> : <ComponentB />}
</div>
);
}
}