文章目录
- 1. 带来了什么
- 2. 创建项目
- 3. 入口文件的改变
- 4. setState
- 5. 条件渲染传异步数据给子组件
- 6. suspense结合异步组件实现条件渲染
- 7. useTransition降级渲染
- 8. useDeferredValue节流处理
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 resultconst callbackPromise = promise.then(ret => {// promise执行成功的,返回成功的状态,并把数据赋值给resultstatus = 'success'result = ret},err => {// 把状态修改为失败,并把错误赋值给resultstatus = '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