React高级用法

737 阅读12分钟

1.目标

  • 高阶组件的用法及封装
  • Hooks详解
  • 异步组件
  • React 18 新特性

知识要点

2.1 高阶组件用法及封装

高阶组件,组件作为参数,返回值也是组件的函数,纯函数,不会修改传入的组件,也不会使用继承来复制其行为,hoc通过组件包装在容器组件中来组成新的组件

  • 抽取重复代码,实现组件复⽤:相同功能组件复⽤
  • 条件渲染,控制组件的渲染逻辑(渲染劫持):权限控制。
  • 捕获/劫持被处理组件的⽣命周期,常⻅场景:组件渲染性能追踪、⽇志打点。

2.1.1 属性代理

  • 返回stateless 组件
  • 返回class 组件
  • 操作props
// 可以通过属性代理,拦截⽗组件传递过来的porps并进⾏处理。
// 返回⼀个⽆状态的函数组件
function HOC(WrappedComponent) {
  const newProps = { type: 'HOC' };
  return props => <WrappedComponent {...props} {...newProps}/>;
}
// 返回⼀个有状态的 class 组件
function HOC(WrappedComponent) {
  return class extends React.Component {
    render() {
      const newProps = { type: 'HOC' };
      return <WrappedComponent {...this.props} {...newProps}/>;
   }
 };
}
​
// 通过属性代理⽆法直接操作原组件的state,可以通过props和cb抽象state
function HOC (WrappedComponent) {
  return class extends React.Component {
    constructor (props) {
      super(props);
      this.state = {
        name: '',
      };
      this.onChange = this.onChange.bind(this);
    }
​
    onChange = (event) => {
      this.setState({
        name: event.target.value,
      });
    };
​
    render () {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onChange,
        },
      };
      return <WrappedComponent {...this.props} {...newProps} />;
    }
  };
}
​
// 使⽤
// @HOC
class Example extends Component {
  render () {
    return <input name='name' {...this.props.name} />;
  }
}
​
export default Example;
​

2.1.2反向继承

const HOC = (WrappedComponent) => {
  return class extends WrappedComponent {
    render() {
      return super.render();
   }
 }
}

通过继承,使用supper, this调用父组件的state,ref, render, 生命周期方法

  • 允许HOC通过this访问到原组件,可以直接读取和操作原组件的state/ref等;

  • 可以通过super.render()获取传⼊组件的render,可以有选择的渲染劫持; 劫持原组件⽣命周期⽅法

  • 读取/操作原组件的state

  • 条件渲染 修改react树

  • 修改react树

    function HOC(WrappedComponent){
      const didMount = WrappedComponent.prototype.componentDidMount;
      
      // 继承了传⼊组件
      return class HOC extends WrappedComponent {
       async componentDidMount(){
          // 劫持 WrappedComponent 组件的⽣命周期
          if (didMount) {
            didMount.apply(this);
         }
           // 将 state 中的 number 值修改成 2
          this.setState({ number: 2 });
       }
        render(){
          //使⽤ super 调⽤传⼊组件的 render ⽅法
             if (this.props.isRender) {
            return super.render();
         } else {
            return <div>暂⽆数据</div>;
         }
          return super.render();
       }
     }
    }
    ​
    

    修改react树

function HigherOrderComponent(WrappedComponent) {
  return class extends WrappedComponent {
    render() {
      const tree = super.render();
      const newProps = {};
      if (tree && tree.type === 'input') {
        newProps.value = 'something here';
     }
      const props = {
        ...tree.props,
        ...newProps,
     };
      const newTree = React.cloneElement(tree, props,
tree.props.children);
      return newTree;
   }
 };
}

2.2 hooks详解

Hooks是react16.8以后新增的钩⼦API; ⽬的:增加代码的可复⽤性,逻辑性,弥补⽆状态组件没有⽣命周期,没有数据管理状态state的缺 陷。

  • 开发友好,可扩展性强,抽离公共的⽅法或组件,Hook 使你在⽆需修改组件结构的情况下复⽤状 态逻辑;
  • 函数式编程,将组件中相互关联的部分根据业务逻辑拆分成更⼩的函数;
  • class更多作为语法糖,没有稳定的提案,且在开发过程中会出现不必要的优化点,Hooks⽆需学习 复杂的函数式或响应式编程技术;

useState

  • ⽀持stateless组件有⾃⼰的state;
  • ⼊参:具体值或⼀个函数
  • 返回值:数组,第⼀项是state值,第⼆项负责派发数据更新,组件渲染
  • setState会让组件重新执⾏
import {useState} from 'react';
​
export const DemoUseState = (props) => {
  let [number, setNumber] = useState(1);
  return (
    <div>
      <span>{number}</span>
      <button onClick={() => {
        setNumber(number + 1);
        console.log(number);
      }}>click me
      </button>
    </div>
  );
};

useEffect

  • 不限制条件,组件每次更新都会触发useEffect --> componentDidUpdate 与 componentwillreceiveprops
  • useEffect 第⼀个参数为处理事件,第⼆个参数接收数组,为限定条件,当数组变化时触发事件, 为[]只在组件初始化时触发;
  • useEffect第⼀个参数接收返回函数,⽤以消除某些副作⽤ --> componentWillUnmount
  • useEffect⽆法直接使⽤async await
  • 渲染组件 useEffect 闪动
import {
  useEffect,
  useRef,
  useState,
} from 'react';
import {getUserInfo} from './apiUtil';
​
export const DemoUseEffect = ({a}) => {
  const [user, setUser] = useState({});
  const [number, setNumber] = useState(0);
  const div = useRef();
  const handleResize = () => {
  };
  // /* useEffect使⽤ ,这⾥如果不加限制 ,会是函数重复执⾏,陷⼊死循环*/
  useEffect(() => {
​
    getUserInfo(a, number).then(res => {
      setUser(res);
    });
    console.log(div.current);
    window.addEventListener('resize', handleResize);
  }, [
    a,
    number,
  ]);
  /* 只有当props->a和state->number改变的时候 ,useEffect副作⽤函数重新执⾏ ,如
果此时数组为空[],证明函数只有在初始化的时候执⾏⼀次相当于componentDidMount */
  return (
    <div ref={div}>
      <span>{user.name}</span>
      <span>{user.age}</span>
      <div onClick={() => setNumber(number + 1)}>{number}</div>
    </div>
  );
};
​
// Bad
useEffect(async ()=>{
  /* 请求数据 */
  const res = await getUserInfo(payload)
},[ a ,number ])
————————————————————————————————————————————————
useEffect(() => {
  // declare the async data fetching function
  const fetchData = async () => {
    const data = await fetch('https://xxx.com');
    const json = await data.json();
    return json;
 }
  // call the function
  const result = fetchData()
   .catch(console.error);;
  // ❌ ⽆效
  setData(result);
}, [])
// 改进版
useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('https://xxx.com');
    const json = await response.json();
    setData(json);
 }
  // call the function
  fetchData()
    // make sure to catch any error
   .catch(console.error);;
}, [])
​

useEffect: 组件更新挂载完成 -> 浏览器dom 绘制完成 -> 执⾏useEffect回调 ;

useLayoutEffect

  • 渲染组件卡顿

useLayoutEffect : 组件更新挂载完成 -> 执⾏useLayoutEffect回调-> 浏览器dom 绘制完成;

export const DemoUseLayoutEffect = ()=>{
  const target = useRef()
  useLayoutEffect(() => {
    /*我们需要在dom绘制之前,移动dom到制定位置*/
    const { x ,y } = getPositon() /* 获取要移动的 x,y坐标 */
    animate(target.current,{ x,y })
  }, []);
  return (
    <div >
      <span ref={ target } className="animate"></span>
    </div>
  )
}

useRef

⽤来获取元素(当前节点的element)、缓存数据; ⼊参可以作为初始值

//获取元素
import {useRef} from 'react';
​
export const DemoUseRef = () => {
  const dom = useRef(null);
  const handerSubmit = () => {
    /* dom 节点*/
    console.log(dom);
  };
  return (<div ref={dom}>
    <p>表单组件</p>
    <button onClick={() => handerSubmit()}>提交</button>
  </div>);
};

useContext

⽤来获取⽗级组件传递过来的context值,这个当前值就是最近的⽗级组件 Provider 的value; 从parent comp获取ctx⽅式; 1. useContext(Context); 2. Context.Consumer;

/* ⽤useContext⽅式 */
const DemoContext = ()=> {
    const value = useContext(Context);
    /* my name is aaa */
 return <div> my name is { value.name }</div>
}
/* ⽤Context.Consumer ⽅式 */
const DemoContext1 = ()=>{
    return <Context.Consumer>
         {/* my name is aaa */}
       { (value)=> <div> my name is { value.name }</div> }
    </Context.Consumer>
}
export default ()=>{
    return <div>
      <Context.Provider value={{ name:'aaa' }} >
        <DemoContext />
        <DemoContext1 />
        </Context.Provider>
  </div>
}

useReducer(fn,state)

入参:fn(state,action)

state:初始化值

返回:更新后的值state

派发更新的dispatch函数,执⾏dispatch会导致组件re-render

import {useReducer} from 'react';
​
export const DemoUseReducer = () => {
  //number 更新后的state值,dispatchNumber为当前的派发函数
  const [number, dispatchNumber] = useReducer((state, action) => {
    //提取dspatchNumber传进来的值
    const {payload, name} = action;
    //return 的值为新的state
    switch (name) {
      case 'a':
        return state + 1;
      case 'b':
        return state - 1;
      case 'c':
        return payload;
    }
    return state;
  }, 0);
  return (<div>
    当前值:{number}
    {/* 派发更新*/}
    <button onClick={() => dispatchNumber({name: 'a'})}>add</button>
    <button onClick={() => dispatchNumber({name: 'b'})}>sub</button>
    <button onClick={() => dispatchNumber({
      name: 'c',
      payload: 66,
    })}>sub
    </button>
  </div>);
};

userMemo

⽤来根据useMemo的第⼆个参数deps(数组)判定是否满⾜当前的限定条件来决定是否执⾏第⼀个 cb;

// selectList 不更新时,不会重新渲染,减少不必要的循环渲染
useMemo(() => (
      <div>{
          selectList.map((i, v) => (
              <span
                  className={style.listSpan}
                  key={v} >
                 {i.patentName}
              </span>
         ))}
      </div>
), [selectList])
​
// listshow, cacheSelectList 不更新时,不会重新渲染⼦组件
useMemo(() => (
    <Modal
        width={'70%'}
        visible={listshow}
        footer={[
            <Button key="back" >取消</Button>,
            <Button
                key="submit"
                type="primary"
             >
                确定
            </Button>
       ]}
    >
     { /* 减少了PatentTable组件的渲染 */ }
        <PatentTable
            getList={getList}
            selectList={selectList}
            cacheSelectList={cacheSelectList}
            setCacheSelectList={setCacheSelectList} />
    </Modal>
 ), [listshow, cacheSelectList])
 
 
//减少组件更新导致函数重新声明
const {useMemo} = require('react');
const {useState} = require('react');
export const DemoUseMemo = () => {
  /* ⽤useMemo 包裹之后的log函数可以避免了每次组件更新再重新声明 ,可以限制上下⽂的
执⾏ */
  const [number, setNumber] = useState(0);
  const newLog = useMemo(() => {
    const log = () => {
      /* 点击span之后 打印出来的number 不是实时更新的number值 */
      console.log(number);
    };
    return log;
    /* [] 没有 number */
  }, [number]);
  return <div>
    <div onClick={() => newLog()}>打印</div>
    <span onClick={() => setNumber(number + 1)}>增加</span>
  </div>;
};
​

useCallback

useMemo返回cb的运⾏结果; useCallback返回cb的函数;

// 1. 由于是pureComponent每⼀次都会重新⽣成新的props函数,这时候就会触发⼦组件的更
新,使⽤React.memo
// 2. useCallback ,必须配合 react.memo pureComponent ,否则不但不会提升性能,
还有可能降低性能
/* ⽤react.memo */
const DemoChildren = React.memo((props)=> {
  /* 只有初始化的时候打印了 ⼦组件更新 */
    console.log('⼦组件更新')
   useEffect(()=>{
       props.getInfo('⼦组件')
   },[])
   return <div>⼦组件</div>
})
const DemoUseCallback=({ id })=>{
    const [number, setNumber] = useState(1)
    /* 此时usecallback的第⼀参数 (sonName)=>{ console.log(sonName) }
     经过处理赋值给 getInfo */
    const getInfo  = useCallback((sonName)=>{
          console.log(sonName)
   },[id])
    return <div>
       {/* 点击按钮触发⽗组件更新 ,但是⼦组件没有更新 */}
        <button onClick={ ()=>setNumber(number+1) } >增加</button>
        <DemoChildren getInfo={getInfo} />
    </div>
}
​

2.3 Hooks 实战注意

useEffect

  • useEffect 中,默认有个共识: useEffect 中使⽤到外部变量,都应该放到第⼆个数组参数中。
// 当props.count 和 count 变化时,上报数据
function Demo(props) {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');
  const [a, setA] = useState('');
  useEffect(() => {
    monitor(props.count, count, text, a);
 }, [props.count, count]);
  return (
    <div>
      <button
        onClick={() => setCount(count => count + 1)}
      >
        click
      </button>
      <input value={text} onChange={e => setText(e.target.value)} />
      <input value={a} onChange={e => setA(e.target.value)} />
    </div>
 )
}

deps参数不能缓解闭包问题

  • 只有变化时,需要重新执⾏ useEffect 的变量,才要放到 deps 中。⽽不是 useEffect ⽤到的变量都放 到 deps 中。
  • 在有延迟调⽤场景时,可以通过 ref 来解决闭包问题。
// 当进⼊⻚⾯ 3s 后,输出当前最新的 count
// Example 1
function Demo() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const timer = setTimeout(() => {
      console.log(count)
   }, 3000);
    return () => {
      clearTimeout(timer);
   }
 }, [])
  return (
    <button
      onClick={() => setCount(count => count + 1)}
    >
      click
    </button>
 )
}
// 输出0// Example 2
useEffect(() => {
    const timer = setTimeout(() => {
      console.log(count)
   }, 3000);
    return () => {
      clearTimeout(timer);
   }
 }, [count])
// 输出1// Example 3
const [count, setCount] = useState(0);
// 通过 ref 来记录最新的 count
const countRef = useRef(count);
countRef.current = count;
useEffect(() => {
  const timer = setTimeout(() => {
    console.log(countRef.current)
 }, 3000);
    return () => {
    clearTimeout(timer);
 }
}, [])

什么情况下会存在闭包?

// 正常情况下,不会存在
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const c = a + b;
useEffect(()=>{
console.log(a, b, c)
}, [a]);
useEffect(()=>{
console.log(a, b, c)
}, [b]);
useEffect(()=>{
console.log(a, b, c)
}, [c]);
// 在延迟调⽤下,会存在闭包
// 1. 使⽤ setTimeout、setInterval、Promise.then 等
// 2. useEffect 的卸载函数
const getUsername = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('John');
   }, 3000);
 })
}
function Demo() {
  const [count, setCount] = useState(0);
  // setTimeout 会造成闭包问题
  useEffect(() => {
    const timer = setTimeout(() => {
      console.log(count);
   }, 3000);
    return () => {
      clearTimeout(timer);
   }
 }, [])
  // setInterval 会造成闭包问题
  useEffect(() => {
    const timer = setInterval(() => {
      console.log(count);
   }, 3000);
    return () => {
      clearInterval(timer);
   }
 }, [])
// Promise.then 会造成闭包问题
  useEffect(() => {
    getUsername().then(() => {
      console.log(count);
   });
 }, [])
  // useEffect 卸载函数会造成闭包问题
  useEffect(() => {
    return () => {
      console.log(count);
   }
 }, []);
  return (
    <button
      onClick={() => setCount(count => count + 1)}
    >
      click
    </button>
 )
}
  // 都返回0
// 组件初始化,此时 count = 0
// 执⾏ useEffect,此时 useEffect 的函数执⾏,JS 引⽤链记录了对 count=0 的引⽤关// 点击 button,count 变化,但对之前的引⽤已经⽆能为⼒了  

尽量不要⽤useCallback

  • useCallback ⼤部分场景没有提升性能
  • useCallback让代码可读性变差

useMemo建议适当使⽤

// 没有使⽤ useMemo
const memoizedValue = computeExpensiveValue(a, b);
// 使⽤ useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
// 如果没有使⽤ useMemo,computeExpensiveValue 会在每⼀次渲染的时候执⾏;
// 如果使⽤了 useMemo,只有在 a 和 b 变化时,才会执⾏⼀次
computeExpensiveValue。
const a = 1;
const b = 2;
const c = useMemo(()=> a + b, [a, b]);
const c = a + b; // 内存消耗少

useState的正确使⽤姿势

  • 能⽤其他状态计算出来就不⽤单独声明状态。⼀个 state 必须不能通过其它 state/props 直接计算 出来,否则就不⽤定义 state
  • 保证数据源唯⼀,在项⽬中同⼀个数据,保证只存储在⼀个地⽅
  • useState 适当合并

2.4⾃定义Hooks

⾃定义Hooks本质上还是实现⼀个函数,关键在于实现逻辑

const [ a, [b, c...] ] = useXXX(arg1,[arg2, ...])

防抖

//防抖
import {
  useEffect,
  useRef,
  useState,
} from 'react';
​
const useDebounce = (fn, ms = 30, deps = []) => {
  let timeout = useRef();
  useEffect(() => {
    if (timeout.current) clearTimeout(timeout.current);
    timeout.current = setTimeout(() => {
      fn();
    }, ms);
  }, deps);
  const cancel = () => {
    clearTimeout(timeout.current);
    timeout = null;
  };
  return [cancel];
};
​
export const DebounceInput = (props) => {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);
  const [cancel] = useDebounce(() => {
    setB(a);
  }, 2000, [a]);
  const changeIpt = (e) => {
    setA(e.target.value);
  };
  return <div>
    <input type='text' onChange={changeIpt}/>
    b:{b}, a:{a}
  </div>;
};

节流

function throttle(func, ms) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > ms) {
            func.apply(context, args);
            previous = now;
       }
   }
}
const useThrottle = (fn, ms = 30, deps = []) => {
    let previous = useRef(0)
    let [time, setTime] = useState(ms)
    useEffect(() => {
        let now = Date.now();
        if (now - previous.current > time) {
            fn();
            previous.current = now;
       }
   }, deps)
    const cancel = () => {
        setTime(0)
   }
  
    return [cancel]
 }
​

setTitle

import { useEffect } from 'react'
const useTitle = (title) => {
    useEffect(() => {
      document.title = title
   }, [])
  
    return
 }
export default useTitle
const App = () => {
    useTitle('new title')
    
    return <div>home</div>
}
​

update hook

import {useState} from 'react';
​
const useUpdate = () => {
  const [, setFlag] = useState();
  const update = () => {
    setFlag(Date.now());
  };
​
  return update;
};
// 实际使⽤
export const DemoUseUpdate = (props) => {
  // ...
  const update = useUpdate();
  return <div>
    {Date.now()}
    <div>
      <button onClick={update}>update</button>
    </div>
  </div>;
};
​

useScroll hooks

import { useState, useEffect } from 'react'
const useScroll = (scrollRef) => {
  const [pos, setPos] = useState([0,0])
  useEffect(() => {
    function handleScroll(e){
      setPos([scrollRef.current.scrollLeft, scrollRef.current.scrollTop])
   }
    scrollRef.current.addEventListener('scroll', handleScroll, false)
    return () => {
      scrollRef.current.removeEventListener('scroll', handleScroll,
false)
   }
 }, [])
  
  return pos
}
export default useScroll
// ⽤法
import React, { useRef } from 'react'
import { useScroll } from 'hooks'
const Home = (props) => {
  const scrollRef = useRef(null)
  const [x, y] = useScroll(scrollRef)
  return <div>
      <div ref={scrollRef}>
        <div className="innerBox"></div>
      </div>
      <div>{ x }, { y }</div>
    </div>
}
​

3.异步组件

  • 在引⼊第三⽅的库的情况下,要避免因体积过⼤导致加 载时间过⻓。
  • 引⼊了 React.lazy 和 React.Suspense 两个API,再配合动态 import() 语法就可以实现 组件代码打包分割和异步加载 传统模式:渲染组件-> 请求数据 -> 再渲染组件 异步模式:请求数据-> 渲染组件;
// demo
import React, { lazy, Suspense } from 'react';
// lazy 和 Suspense 配套使⽤,react原⽣⽀持代码分割
const About = lazy(() => import(/* webpackChunkName: "about"
*/'./About'));
class App extends React.Component {
    render() {
        return (
            <div className="App">
                <h1>App</h1>
                <Suspense fallback={<div>loading</div>}>
                    <About />
                </Suspense>
            </div>
       );
   }
}
export default App;

动态impourt

  • 相对于静态import的 import XX from XXX,动态import指在运⾏时加载
  • 实现Promise规范
import('./test.js').then(test => {
    // ...
});

错误边界

错误边界是⼀种 React 组件,错误边界在 渲染期间、⽣命周期⽅法和整个组件树的构造函数 中捕获错 误,且会渲染出备⽤UI⽽不是崩溃的组件。

import {useEffect} from 'react';
import * as React from 'react';
​
const {useState} = require('react');
​
class DemoErrorBoundary extends React.Component {
  constructor (props) {
    super(props);
    this.state = {hasError: false};
  }
​
  static getDerivedStateFromError (error) {
    // 更新 state 使下⼀次渲染能够显示降级后的 UI
    return {hasError: true};
  }
​
  componentDidCatch (error, errorInfo) {
    // 你同样可以将错误⽇志上报给服务器
    console.log(error, errorInfo);
  }
​
​
  render () {
    if (this.state.hasError) {
      // 你可以⾃定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}
​
export const DemoUseErrorBoundary = () => {
  const [count, setCount] = useState(1);
  useEffect(() => {
    if (count === 3) {
      throw new Error('I crashed!');
    }
  }, [count]);
  return (
    <DemoErrorBoundary>
      <h1>App</h1>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>add
      </button>
    </DemoErrorBoundary>
  );
};
​
// comp Appexport class DemoUseErrorBoundary1 extends React.Component {
  state = {
    count: 1,
  };
​
  render () {
    const {count} = this.state;
    if (count === 3) {
      throw new Error('I crashed!');
    }
    return (
      <DemoErrorBoundary>
        <h1>App</h1>
        <p>{count}</p>
        <button onClick={() => this.setState({
          count: count + 1,
        })}>add
        </button>
      </DemoErrorBoundary>
    );
  }
}

⼿写异步组件

  1. lazy wrapper住异步组件,React第⼀次加载组件的时候,异步组件会发起请求,并且抛出异常,终 ⽌渲染;
  2. Suspense⾥有componentDidCatch⽣命周期函数,异步组件抛出异常会触发这个函数,然后改变 状态使其渲染fallback参数传⼊的组件;
  3. 异步组件的请求成功返回之后,Suspense组件再次改变状态使其渲染正常⼦组件(即异步组件);
// comp About
const About = lazy(() => new Promise(resolve => {
    setTimeout(() => {
        resolve({
            default: <div>component content</div>
       })
   }, 1000)
}))
// comp Suspense
import React from 'react'
class Suspense extends React.PureComponent {
    /**
     * isRender 异步组件是否就绪,可以渲染
     */
    state = {
        isRender: true
   }
    componentDidCatch(e) {
        this.setState({ isRender: false })
        e.promise.then(() => {
            /* 数据请求后,渲染真实组件 */
            this.setState({ isRender: true })
       })
   }
    render() {
        const { fallback, children } = this.props
        const { isRender } = this.state
        return isRender ? children : fallback
   }
}
export default Suspense
// comp lazy
import React, { useEffect } from 'react'
export function lazy(fn) {
    const fetcher = {
        status: 'pending',
        result: null,
        promise: null,
   }
    return function MyComponent() {
        const getDataPromise = fn()
        fetcher.promise = getDataPromise
        getDataPromise.then(res => {
                fetcher.status = 'resolved'
            fetcher.result = res.default
       })
        useEffect(() => {
            if (fetcher.status === 'pending') {
                throw fetcher
           }
       }, [])
        if (fetcher.status === 'resolved') {
            return fetcher.result
       }
        return null
   }
}
// 实现的效果与React⽀持内容保持⼀致
import React, {Suspese, lazy} from 'react'
const About= lazy(() => { import('../About') });
class App extends React.Component {
  render() {
    /**
     * 1. 使⽤ React.Lazy 和 import() 来引⼊组件
     * 2. 使⽤<React.Suspense></React.Suspense>来做异步组件的⽗组件,并使⽤
fallback 来实现组件未加载完成时展示信息
     * 3. fallback 可以传⼊html,也可以⾃⾏封装⼀个统⼀的提示组件
     */
    return (
      <div>
        <Suspense
          fallback={
            <Loading />
         }
        >
          <About />
        </Suspense>
      </div>
   )
 }
}
export default ReactComp;
    

18特性

  • Automatic batching(⾃动批量更新) 2.
  • startTransition
  • ⽀持 React.lazy 的SSR架构
  • Concurrent Mode (并发渲染、可选)