react函数组件

1,526 阅读4分钟

函数组件的写法

const App2 = (props) => {
	const {n,setN}=React.useState(0) //初始化state Hooks API
  return <div></div>;
};

函数组件与class组件的不同

  • 没有生命周期
  • 简洁好写(优点)
  • 没有this(这个实际上算是优点)
  • 没有state

Hook API

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

state

为了解决函数组件没有state的问题,react在hook上做了useState的API

useState函数返回两个值,一个是初始化的数据,还有一个是设置该值的函数。它跟class组件上面的this.state是一样的。

但是要注意,它并像this.state会帮助我们自动合并,而且,初始化的值也不一定非要是一个对象

写法

const [n,setN]=React.useState(0) //初始化n为0
const [state,setState]=React.useState({n:0}) //state={n:0}

上面的setNsetState就类似于this.setState(),不过名字可以随便写,功能是一样的。

示例

以一个点击+1为例

const Example=(props)=>{
	const [n,setN]=React.useState(0)
   const add=()=>{
   	setN(n+1)
   }
   return <button onClick={add}>点击加1,此时n为{n}</button>
}

是不是超级简单?如果以后要创建很多state怎么办?可以使用对象,但是要注意手动合并

const Example=(props)=>{
  const [state,setState]=React.useState({n:0,m:0})
  const add=()=>{
  	//setState({n:state.n+1})   //** wrong!! **
  	setState({...state,n:state.n+1})   //** right!! **
  }
  return <>
  <div>m:{state.m}</div>
  <div>n:{state.n}</div>
  <button onClick={add}>点击加1,此时n为{state.n}</button>
  </>
}

上面使用了 ...运算符进行拷贝原来的state.mstate.n,然后覆盖了state.n,这种写法是正确的

Effect

react提供了React.useEffect这个API帮助我们模拟 class组件里面的生命周期。

React.useEffect就是一个函数,每次render之后就会触发这个函数,所以当我们这样写

React.useEffect(()=>{
    console.log('render了一次')})

就会在每次render时触发。包括第一次render

模拟componentDidMount

React.useEffect(()=>{
    console.log('第一次render')},[])

模拟componentWillUnmount

 React.useEffect(()=>{
    return ()=>{
   	 console.log('删除dom时执行')
        }
        })

模拟componentDidUpdate

  React.useEffect(() => {
   console.log("n变了");
 }, [state.n]); // 每次state.n变了都会执行钩子
 //如果想要所有数据变化都执行,那么就不写第二个参数就行

注意:第一次渲染时,也会执行上面的钩子,因为第一次state.n从undefined到初始值也是一个update,我们可以增加一些逻辑来变相达成目标

const App2 = (props) => {
 const [n, setN] = React.useState(0); //第一次为0
 const add = () => {
   setN(n + 1); // 每次点击后加1
 };
 React.useEffect(() => {
   if (n > 0) {  // 当它>0时才执行,因为第一次为0
     console.log("n变了");
   }
 }, [n]);
 return (
   <>
     <div>n:{n}</div>
     <button onClick={add}>点击加1,此时n为{n}</button>
   </>
 );
};

上面的代码由于逻辑是初始值为0,每次点击后都+1,所以我增加了n>0这个判断,来避免第一次执行

但很多时候,由于应用场景的复杂导致我们没有办法来进行这样的逻辑判断。

所以更好的办法是自定义一个Hook函数来进行通用,而不是每次都根据业务逻辑来修改代码

自定义Hook

自定义Hook要求使用use开头的函数名以保持react函数组件风格的统一,不这样做就会报错

自定义模拟componentDidUpdate

我们采用useUpdate这样的函数名以避免react报错,目前的需求是这样的

  • 第一次渲染时,不要执行
  • 通用函数,以后都可以用
  • 要求能监控到对应的数据变化
  • 保持React.useEffect风格的统一,使用[]作为第二个参数,第一个参数是函数 所以可以根据react的API写一个函数
const useUpdate = (fn, array) => {
  const [count, setCount] = React.useState(0); //初始化render次数为0
  React.useEffect(() => {
    setCount(count + 1);//当我们要监控的数据array变化时,就让count次数+1
  }, array);
  React.useEffect(() => {
    if (count > 1) {//这里为什么要写>1?因为不管怎样,都会执行useEffect
      fn();
    }
  }, [count]);//当次数+1时,执行函数
};
// 调用示例
useUpdate(()=>{console,log('xx')},[n])

上面的代码会有警告⚠️,虽然写法没错,但是react编译时,会有点问题,所以编译报了警告

按照警告,我们改成这样

const useUpdate = (fn, array) => {
  const [count, setCount] = React.useState(0); //初始化render次数为0
  React.useEffect(() => {
    setCount((x) => x + 1); //react 提示要改成这样
  }, [array]); //提示这里的参数一定要写成这样
  React.useEffect(() => {
    if (count > 1) {
      fn();
    }
  }, [count, fn]); //提示要加入fn
};
//现在调用时,传入的参数只能是单个的值
// 调用示例
useUpdate(()=>{},n)

参考文档

zh-hans.reactjs.org/docs/hooks-…

zh-hans.reactjs.org/docs/hooks-…