setState()
简单案例:
案例代码如下:
import React from "react";
//UI组件
export default class Person extends React.Component{
state = {count:0}
increment = () => {
const {count} = this.state
this.setState({count:count+1})
console.log(this.state.count) // 0 此时页面显示1
}
render(){
return(
<div>
<h2>当前求和为:{this.state.count}</h2>
<button onClick={this.increment}>点我+1</button>
</div>
)
}
}
1.setState两种书写形式
- 第一个是第一个参数为对象类型的形式(经常使用)
this.setState({count:count+1})
- 第二个是第一个参数为updater 函数中(有两个参数state和props)
this.setState((state, props) => {
return {counter: state.counter+1};
});
2.setState做了什么
- setState(会对一个组件的 state 对象安排一次更新。当 state 改变了,该组件就会重新渲染。
3.setState更新后,随后打印的值未变,但页面值改变了?
- setState()本身是同步方法,但是react中状态更新完后引起的动作是异步的。所以官网上说:“调用 setState 其实是异步的 —— 不要指望在调用 setState 之后,this.state 会立即映射为新的值。”
- 将 setState() 视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。
4.setState更新后如何马上拿到最新值?
- 对象式setState更新:setState函数可以传入第二个参数(回调函数),此回调函数是在页面重新render后拿到count值,所以在回调函数里可以去到更改完成后的最新值
increment = () => {
const {count} = this.state
this.setState({count:count+1},()=>{
console.log(this.state.count) // 1 和页面显示的值一样
})
}
- componentDidUpdate生命钩子函数可以拿到更新过的state
lazyLoad
使用之前:
import OtherComponent from './OtherComponent';
使用之后:
import React, {lazy} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));
此代码将会在组件首次渲染时,自动导入包含 OtherComponent 组件的包。
lazy函数是什么?
- React.lazy 函数能让你像渲染常规组件一样处理动态引入(的组件)
- React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 default export 的 React 组件。
- 然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级 。
- Suspense 组件的fallback 属性接受任何在组件加载过程中你想展示的 React 元素(loading页面)。你可以将 Suspense 组件置于懒加载组件之上的任何位置。你甚至可以用一个 Suspense 组件包裹多个懒加载组件。
代码案例
上面的语言描述只读是理解不了的,还是得结合代码一块理解,话不多说,上代码!
用lazy函数结合Suspense组件实现路由懒加载
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
上面代码通过lazy函数引入的组件,页面一上来并不全部被渲染,当点击进入某个组件时才会去加载组件。点击加载组件时,如果网速较慢,会显示Suspense组件fallback属性引入的加载中组件或者其他React 元素。
为什么这样做?现在只是两个组件,那一百个组件呢,如果不适用懒加载,一上来首次渲染,100个组件需要全部都被渲染,加长了首屏加载时间。而采用lazy懒加载,这样可以更好的提高了用户体验,首屏组件加载速度更快一些,解决白屏问题。
Hook
React Hook 是什么?
- Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
三个常用的Hook
- state Hook:React.useState()
- Effect Hook:React.useEffect()
- Ref Hook:react.useRef()
state Hook
- state Hook让函数组件也可以有state状态,并进行状态数据的读写操作
- 语法:const {xxx,setXxx} = React.useState(initValue)
- useState()说明:
参数:第一次初始化指定的值在内部作缓存
返回值:包含2个元素的数据,第一个参数为函数,第2个为内部当前指定新的状态值,内部用其覆盖原来的状态值
- setXxx()2种写法:
setXxx(newValue):参数为非函数值,直接指定新的状态值。内部用其覆盖原来的状态值
setXxx( value => newValue):参数为函数,接受原来的状态值,返回新的状态值,内部用其覆盖原来的状态值
还是第一个点击+1的案例:
import React from 'react'
function demo(){
const [count,setCount] = React.useState(0)
functon increment(){
setCount(count+1)
}
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={increment}>点我+1</button>
</div>
)
}
Effect Hook
- Effect Hook可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
- React中的副作用操作:
发ajax请求数据获取
设置订阅/启动定时器
手动更改真实DOM
- 语法和说明
useEffect(() => {
//在此可以执行任何带副作用的操作
return () => {
//在此做一些收尾工作,比如清除定时器/取消订阅
}
},[stateValue])
{/*第2个参数[stateValue]有检测的意思,如果指定的是[],
回调函数只会在第一次render()后执行,如果不传第2个参数[]的话,
相当于检测所欲satte,在state发生变化时也会触发回调函数*/}
- 可以把useEffect Hook看作如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
挂载后自动一直+1的案例:
import React from 'react'
function demo(){
const [count,setCount] = React.useState(0)
React.useEffect(() => {
const timer = setInterval(() => {
setCount(count+1)
},500)
return () => {
clearInterval(timer)
}
},[])
function unMount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={unMount}>点击卸载组件</button>
</div>
)
}
Ref Hook
- Ref Hook可以在函数组件中存储/查找组件内部标签或任意其他数据
- 语法: const myRef = React.useRef()
- 作用:保存标签对象,功能与React.createRef()一样
import React from 'react'
function demo(){
const myRef = React.useRef()
show(){
alert(myRef.current.value)
}
return(
<div>
<input type="text" ref={myRef]>
<button onClick = {show}>点击提示input框信息</button
</div>
)
}
Fragment
- 使用:< Fragment > </ Fragment >
- 作用:可以不用必须有一个真实DOM跟标签了
来个简单案例:
class Count extends React.Component{
render(){
return(
<div> {/*包<div>的原因是下只有一个根元素,其实可以用<Fragment>代替,控制台打印就不会被显示此标签*/}
<div>...
<input>...
...
</div>
)
}
}
Context
理解
- 一种组件通信的方式,常用于祖组件与后代组件之间的通信
使用
- 创建Context容器对象:const XxxContext = React.createContext()
- 渲染子组件时,外面包裹XxxContext .Provider,通过value属性给后代组件传递数据
<XxxContext .Provider value={数据}>
</XxxContext .Provider>
- 后代组件读取数据
//第一种方式:仅限于类组件
static contextType = MyContext //声明需要接收context
this.context
//第二种方式:函数组件和类组件都可以使用
<XxxContext .Consumer value={数据}>
{
value => { //value 就是context中的value数据
//显示内容
}
}
</XxxContext .Consumer>
案例
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
//祖组件
class A extends React.Component{
state = {
useName:'A组件'
}
render(){
const {useName} = this.state
return(
<div>
<h2>我是A组件,用户名为:{useName}</h2>
<Provider value={useName}>
<B/>
</Provider>
</div>
)
}
}
//子组件
class B extends React.Component{
render(){
return(
<div>
<C />
<D />
<h2>我是B组件</h2>
<h2>我从A组件接收到的用户名是:{}</h2>
</div>
)
}
}
//孙组件(类组件)
class C extends React.Component{
static contextType = MyContext //声明需要接收context
render(){
return(
<div>
<h2>我是C组件</h2>
<h2>我从A组件接收到的用户名是:{this.context}</h2>
</div>
)
}
}
//孙组件(函数组件)
function D(){
return(
<div>
<div>
<h2>我是D组件</h2>
<h2>我从A组件接收到的用户名是:
<Consumer>
{
value => {
retrun `${value.useName}`
}
}
<Consumer/>
</h2>
</div>
</div>
)
}