文章目录
1. 带来了什么
- 改进已有属性,如自动批量处理【setState】、改进Suspense、组件返回undefined不再报错等
- 支持Concurrent模式,带来新的API,如useTransition、useDeferredValue等
注意:React升级对于开发者而言,无需重写代码就能够使用React18
2. 创建项目
用以下几种方案创建出来的项目使用为react18版本:
npx create-react-app myapp
npm init react-app myapp
yarn create react-app myapp
注意:
- nodejs版本一定要为16.x及以上版本,如果你用的是win笔记本,则操作系统不能低于win10
- react18中的webpack版本为5版本
3. 入口文件的改变
import React from 'react'
// react18它引入ReactDOM类的位置发生改变
import ReactDOM from 'react-dom/client'
// 在react18之后,不要用此方案来引入ReactDOM类
// import ReactDOM from 'react-dom'
import App from './App'
// 把虚拟dom挂载到真实dom中的方法也发生了改变 由原来的render方法,变为 createRoot(dom节点).render(<App/>)
// 支持Concurrent模式[批处理,让setState都为异步] -- 提升性能
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// 也可以使用 React17 中的方案
// 不支持Concurrent模式,所以在react18之后就不要写此方案
// ReactDOM.render(
// <React.StrictMode>
// <App />
// </React.StrictMode>,
// document.getElementById('root')
// )
4. setState
在 react18 之后,setState 都为异步,无论写在什么样的语法环境中。
import React, {
Component } from 'react'
class App extends Component {
state = {
count: 100
}
addCount = () => {
// 异步的,写成回调函数的方式,可以获得最新的数据状态
this.setState(
state => ({
count: state.count + 1 }),
() => console.log(this.state.count)
)
// 此方案在react18之前,它里面的操作是同步的,但在react18之后,它都为concurrent模式,都为异步
// setTimeout(() => {
// this.setState(state => ({ count: state.count + 1 }))
// console.log(this.state.count)
// }, 1)
}
render() {
return (
<div>
<h3>{
this.state.count}</h3>
<button onClick={
this.addCount}>累加count</button>
</div>
)
}
}
export default App
如果在 react18 中,我们想要让 setState 变为同步,我们可以使用 flushSync 方法:
import React, {
Component } from 'react'
// flushSync它方法就可以让里面的操作为同步
import {
flushSync } from 'react-dom'
class App extends Component {
state = {
count: 100
}
addCount = () => {
// react18中,就想让setState它为同步【可以,但不要在生产中去用,不建议】
// setState它就是同步的
flushSync(() => {
this.setState(state => ({
count: state.count + 1 }))
})
// 因为setState放在flushSync方法里面了,则它现在是一个同步的,所以在此处可以得到最新的数据
console.log(this.state.count)
}
render() {
return (
<div>
<h3>{
this.state.count}</h3>
<button onClick={
this.addCount}>累加count</button>
</div>
)
}
}
export default App
5. 条件渲染传异步数据给子组件
mock 数据:
[
{
"id": 1, "name": "张三" },
{
"id": 2, "name": "英子" },
{
"id": 3, "name": "乐乐" }
]
父组件:
import React, {
useEffect, useState } from 'react'
import User from './pages/User'
const fetchUser = async () => {
let ret = await (await fetch('/users.json')).json()
return ret
}
const App = () => {
let [data, setData] = useState([])
useEffect(() => {
fetchUser().then(ret => setData(ret))
}, [])
return (
<div>
{
/* 条件渲染 */}
{
data.length == 0 ? <div>加载中...</div> : <User data={
data} />}
</div>
)
}
export default App
子组件:
import React from 'react'
const User = ({
data }) => {
return (
<div>
<ul>
{
data.map(item => (
<li key={
item.id}>{
item.name}</li>
))}
</ul>
</div>
)
}
export default User
6. suspense结合异步组件实现条件渲染
父组件:
import React, {
Suspense, useEffect, useState } from 'react'
import User from './pages/User'
// 网络请求
// 返回值为 Promise
const fetchUser = async () => {
let ret = await (await fetch('/users.json')).json()
return ret
}
// 创建一个用于解析promise中数据的方法 仿promise的3个状态
const wrapperPromise = promise => {
// 定义一个promise的状态
let status = 'pending'
// 它就是promise解析出来的数据接受的变量
let result
const callbackPromise = promise.then(
ret => {
// promise执行成功的,返回成功的状态,并把数据赋值给result
status = 'success'
result = ret
},
err => {
// 把状态修改为失败,并把错误赋值给result
status = 'error'
result = err
}
)
return {
// 此方法中,才是把数据获取到
read() {
if (status === 'pending') {
// 抛一个异常,这样它就会再来执行,此时就会有上一次的结果
throw callbackPromise
} else if (status === 'success') {
return result
} else if (status === 'error') {
return result
}
}
}
}
const App = () => {
let [data, setData] = useState(wrapperPromise(fetchUser()))
return (
<div>
<Suspense fallback={
<div>加载中 .......................................</div>}>
<User users={
data} />
</Suspense>
</div>
)
}
export default App
子组件:
import React from 'react'
// 函数组件,它需要返回jsx而不是一个promise对象
const User = ({
users }) => {
// 通过此方法把promise中的数据读取出来
let data = users.read()
return (
<div>
<ul>
{
data.map(item => (
<li key={
item.id}>{
item.name}</li>
))}
</ul>
</div>
)
}
export default User
7. useTransition降级渲染
概述:
如果你有很多没那么着急的内容要渲染更新就可以使用此hook函数。它可以对于更新渲染进行降级,提高更重要的组件的提前渲染
使用:
import React, {
useState, useTransition } from 'react'
// 可以对于更新渲染进行降级,提高更重要的组件的提前渲染
const App = () => {
let [count, setCount] = useState(100)
// isPending 如果在更新等待渲染时isPending为true,没有等待更新不渲染时为false
// startTransition 它是一个函数,在里面写的更新数据会进行降级
let [isPending, startTransition] = useTransition()
const addCount = () => {
// 对于更新count的数据时行了降级,更新也就会降级
startTransition(() => {
setCount(v => v + 1)
})
}
return (
<div>
{
/* count更新它没有哪么的着急,可以让别的数据更新渲染先执行 */}
<h3>{
isPending ? '加载中...' : count}</h3>
<button onClick={
addCount}>++++++</button>
</div>
)
}
export default App
8. useDeferredValue节流处理
概述:
该方法使得我们可以延迟更新某个不那么重要的部分,有节流防抖效果。可以将原来的更新进行推迟渲染,把重要的更新数据推到前面去更新渲染去。
使用:
父组件:
import React, {
useState, useDeferredValue } from 'react'
import Search from './pages/Search'
const App = () => {
let [kw, setKw] = useState('')
// 让数据更新降级,起到了节流的效果,让渲染平滑一些
let deferredValue = useDeferredValue(kw)
return (
<div>
<h3>{
kw}</h3>
<input value={
kw} onChange={
e => setKw(e.target.value.trim())} />
<Search kw={
deferredValue} />
</div>
)
}
export default App
子组件:
import React from 'react'
const Search = ({
kw }) => {
console.log(kw)
const data = Array(1000)
.fill('')
.map((_, index) => {
return '搜索 -- ' + index + ' -- ' + kw
})
return (
<div>
<ul>
{
data.map(item => (
<li key={
item}>{
item}</li>
))}
</ul>
</div>
)
}
export default Search