最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react hooks基本使用方法

    正文概述 掘金(一葉)   2020-12-30   409

    react hooks

    eject

    1. eject是讲react-script集合起来,方便了我们运行单页面程序,但如果我们要编译多页面,就需要修改react-script ,但它已经被封装起来不方便改动,所以有了eject,使用eject可以将react-script拆开到项目根目录方便我们修改,react-script拆开后多了webpack的配置文件和一些js代码.

    2. eject不能逆向执行,而且没有重复命令

    Context(class 组件)

    Context在react可以跨组件传递变量,比如从爷爷组件到孙子组件传递变量,尽量少使用context,它会破坏组件的独立性

    用法api

    1. createContext

      import React, { Component, createContext } from "react";
      import "./App.css";
      const BatteryContext = createContext();
      class Middle extends React.Component {
        render() {
          return <Leaf />;
        }
      }
      class Leaf extends Component {
        render() {
          return (
            <BatteryContext.Consumer>
              {(battery) => <h1>{battery}</h1>}
            </BatteryContext.Consumer>
          );
        }
      }
         
      class App extends React.Component {
        state = {
          battery: 60,
        };
        render() {
          return (
            <div className="App">
              <BatteryContext.Provider value={this.state.battery}>
                <button
                  onClick={() => {
                    this.setState({ battery: this.state.battery - 1 });
                  }}
                >
                  Press
                </button>
                <Middle></Middle>
              </BatteryContext.Provider>
            </div>
          );
        }
      }
         
      export default App;
         
      

      由于BatteryContext.Consumer 有点繁琐,因此可以用this.context改进,用法如下

      import React, { Component, createContext } from "react";
      import "./App.css";
      const BatteryContext = createContext();
      class Middle extends React.Component {
        render() {
          return <Leaf />;
        }
      }
      class Leaf extends Component {
        static contextType = BatteryContext;//被修改的部分就在这个class中
           
        render() {
          const battery = this.context;
          return <h1>{battery}</h1>;
        }
      }
         
      class App extends React.Component {
        state = {
          battery: 60,
          online: false,
        };
        render() {
          const { online } = this.state;
          return (
            <div className="App">
              <BatteryContext.Provider value={this.state.battery}>
                <button
                  onClick={() => {
                    this.setState({ battery: this.state.battery - 1 });
                  }}
                >
                  Press
                </button>
                <Middle></Middle>
              </BatteryContext.Provider>
            </div>
          );
        }
      }
         
      export default App;
         
      

    lazy

    mpa是多页面,pwa渐进式应用;spa单页面应用

    lazy是将指定组件的导入行为封装成react组件

    页面上的图片只有到了图片的位置才去加载,这样可以节省流量

    Webpack - Code Splitting 提供了这样一种能力,把一个页面的所有js模块人为划分为多个,分时导入,这样就需要用import

    import 有两个应用场景,

    1. import * from ' ' 引用静态资源(最主要的使用场景)
    2. import还有另一个场景,动态导入模块, import(‘./detail.js’).then(…),webpack遇到这个会自动的拆分代码,一般情况下页面不会主动加载,只有使用到了的时候页面才回去加载它
    3. app.jsx
    import React, { Component, lazy,Suspense } from "react";
    import "./App.css";
    // Code Splitting可以支持自定义命名 像这样 /* webpackChunkName:"about"*/ 生成的名字为about.chunk.js
    const About = lazy(() => import(/* webpackChunkName:"about"*/"./About")); //lazy返回一个组件
    //ErrorBoundary 错误边界
    // componentDidCatch在错误边界的应用
    class App extends Component {
      state={
        hasError:false
      }
      // componentDidCatch(){//任何组件加载错误时候自动调用
      //   this.setState({
      //     hasError:true
      //   })
      // }
      static getDerivedStateFromError(){//效果和上面类似,出错的时候会用此函数返回的state来setState,写法更加优雅而已
        return {
          hasError:true
        }
      }
      render() {
        //fallback中传入一个组件或者节点
        if(this.state.hasError){//当lazy组件不能加载时就会显示下面的组件
          return <div>出错了</div>
        }
        return (
          <div>
          
            <Suspense fallback={<div>loading</div>}>
              <About></About>
            </Suspense>
          </div>
        );
      }
    }
    
    export default App;
    
    

    About.jsx

    import React, { Component } from 'react';
    class About extends Component {
      state = {  }
      render() { 
        return ( <div>about</div> );
      }
    }
     
    export default About;
    

    memo

    原理是和class组件一样

    解决react运行的效率问题

    避免重新渲染的几种方式

    1. import React, { Component,PureComponent} from "react";
         
      class Foo extends Component {
        shouldComponentUpdate(nextProps, nextState) { //避免不必要的重新渲染,只有props改变了才重新渲染
          if(nextProps.name===this.props.name){
            return false;
          }else{
            return true
          }
           
        }
        render() { 
          console.log('foo render')
          return ( null );
        }
      }
          
         
      class App extends Component {
        state = {count:0}
        render() { 
          const {count} = this.state;
          return ( <div>
          <button onClick={()=>{this.setState({count:count+1})}}>add</button>
          <Foo name="Mike"></Foo> 
          </div>
          );
        }
      }
          
      export default App;
      
    2. PureComponent

      import React, { Component, PureComponent } from "react";
         
      class Foo extends PureComponent {
        //用了pureCompoent可以实现不必要的更新,只有props变化才会重新渲染,比如父组件更新count但是Foo不会更新
        //有局限,这个是浅比较,如果传入props内部属性变化不会重新渲染
        render() {
          console.log("foo render");
          return <div>{this.props.person.age}</div>;
        }
      }
         
      class App extends Component {
        state = { count: 0, person: { age: 1 } };
        render() {
          const { count,person } = this.state;
          return (
            <div>
              <button
                onClick={() => {
                  this.setState({ count: count + 1 });
                  person.age+=1;
                  this.setState({
                    person
                  })
                }}
              >
                add
              </button>
              <Foo name="Mike" person={person}></Foo>
              <div>count:{count}</div>
            </div>
          );
        }
      }
         
      export default App;
         
      

      如果Foo传入匿名回调函数在父组件更新的时候Foo也会更新,因为匿名函数的地址可能不一样,如下所示

      <Foo name="Mike" person={person} cb={()=>{}}></Foo>
      

      如果传入的不是匿名函数而是已经定义好的函数就不会有这种情况,如下所示

      <Foo name="Mike" person={person} cb={this.callback}></Foo>
      

      memo原理就是上面this.callback的方式来保持组件不更新,不同的是memo用于函数组件中

    memo用法

    import React, { Component, memo } from "react";
    
      //更改为function组件后的代码
      const Foo = memo(function Foo(props) {
        console.log("foo render");
        return <div>{this.props.person.age}</div>;
      })
    
    
    class App extends Component {
      state = { count: 0, person: { age: 1 } };
      callback(){}
      render() {
        const { count,person } = this.state;
        return (
          <div>
            <button
              onClick={() => {
                this.setState({ count: count + 1 });
                person.age+=1;
                this.setState({
                  person
                })
              }}
            >
              add
            </button>
            <Foo name="Mike" person={person} cb={this.callback}></Foo>
            <div>count:{count}</div>
          </div>
        );
      }
    }
    
    export default App;
    
    

    用法几乎一样,只是把

    class Foo extends PureComponent {
      //用了pureCompoent可以实现不必要的更新,只有props变化才会重新渲染,比如父组件更新count但是Foo不会更新
      //有局限,这个是浅比较,如果传入props内部属性变化不会重新渲染
      render() {
        console.log("foo render");
        return <div>{this.props.person.age}</div>;
      }
    }
    

    变成了

     const Foo = memo(function Foo(props) {
        console.log("foo render");
        return <div>{this.props.person.age}</div>;
      })
    

    hooks

    ![image-20200413160337724](react hooks.assets/image-20200413160337724.png)

    ![image-20200413160452789](react hooks.assets/image-20200413160452789.png)

    ![image-20200413160510881](react hooks.assets/image-20200413160510881.png)

    ![image-20200413160525574](react hooks.assets/image-20200413160525574.png)

    ![image-20200413160736757](react hooks.assets/image-20200413160736757.png)

    副作用在这里不是坏的,是指除了渲染之外的操作,比如数据持久化,http请求,绑定解绑事件

    useState

    每次渲染要执行相同次数的useState 和class的state差不多,当传入函数时只在组件第一次渲染执行

    const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      const [size, setSize] = useState({
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight,
      });
    

    useEffect

    相当于一个ComponentWillMount和ComponentWillUnmount

    useEffect(() => {
        console.log(size)
        window.addEventListener("resize", onResize, false);
        return () => {
          window.removeEventListener("resize", onResize, false);
        };
      },[]);
    

    useEffect的两个特例,第二个参数不传是每次渲染都会更新,传空数组就只更新一次.

    完整代码

    import React from "react";
    import { useState, useEffect } from "react";
    // class App extends Component {
    //   state = { count:0 }
    //   render() {
    //     const {count} = this.state;
    //     return ( <button onClick={()=>{this.setState({count:count+1})}}>
    //       count:{count}
    //     </button> );
    //   }
    // }
    
    function App() {
      const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      const [size, setSize] = useState({
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight,
      });
      const onResize = () => {
        setSize({
          width: document.documentElement.clientWidth,
          height: document.documentElement.clientHeight,
        });
      };
      useEffect(() => {
        console.log('count change')
        document.title = count;
      });
      useEffect(() => {
        console.log(size)
        window.addEventListener("resize", onResize, false);
        return () => {
          window.removeEventListener("resize", onResize, false);
        };
      },[]);
      return (
        <button
          onClick={(a) => {
            setCount(count + 1);
          }}
        >
          Click:({count})
          width:({size.width})xheight:({size.height})
        </button>
      );
    }
    export default App;
    
    

    Context Hooks(function组件) 和useContext

    类组件的两种context

    1. consumer
    2. contextType

    function组件有一种,useContext()

    对比:

    - Consumer可以多层嵌套,可以使用到多个context的值,但是使用不优雅
    - contextType只有一个 所以一个类组件只能拿到一个context值,使用比较方便
    - 函数组件的useContext可以获取多个context的值,使用方便优雅
    

    例子:

    import React from "react";
    import { useState, createContext,Component,useContext } from "react";
    
    const countContext = createContext();
    class Foo extends Component {
      render() { 
        //Consumer可以多层嵌套,可以使用到多个context的值,但是使用不优雅
        return ( 
          <countContext.Consumer>
            {count=><h1>{count}</h1>}
          </countContext.Consumer>
         );
      }
    }
    class Bar extends Component {
      static contextType = countContext; 
      //contextType只有一个 所以一个类组件只能拿到一个context值
      render() { 
       const count = this.context;
        return ( <h1>{count}</h1> );
      }
    }
    function Counter(){
      const count = useContext(countContext);
      //函数组件可以获取多个context的值,使用方便优雅
      return ( <h1>
        {count}
      </h1> )
    } 
    
    function App() {
      const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      return (
        <div>
          <button
            onClick={(a) => {
              setCount(count + 1);
            }}
          >
            Click:({count})
          </button>
          <countContext.Provider value={count}>
            <Foo></Foo>
            <Bar></Bar>
            <Counter></Counter>
          </countContext.Provider>
        </div>
      );
    }
    export default App;
    
    

    Memo 和 Callback Hooks (useCallback)

    useMemo是针对一个函数逻辑是否重复执行

    memo是针对一个组件的渲染是否重复执行

    useMemo和useEffect类似,第一个参数是要执行的逻辑函数,第二个参数是依赖的变量数组

    useMemo会执行函数并返回值(如果有return值)

    import React from "react";
    import { useState,useMemo } from "react";
    
    function Counter(props) {
      //函数组件可以获取多个context的值,使用方便优雅
      return <h1>{props.count}</h1>;
    }
    
    function App() {
      const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return count*2;
      },[count===3])
      //第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
      return (
        <div>
          <button
            onClick={(a) => {
              setCount(count + 1);
            }}
          >
            Click:({count}) double:({double})
          </button>
    
          <Counter count={count}></Counter>
        </div>
      );
    }
    export default App;
    
    

    如果useMemo返回的是一个函数,那么等价于useCallback.作用是使函数组件重新渲染时内部的函数不发生变化

    useMemo(()=>fn) === useCallback(fn)

    import React from "react";
    import { useState,useMemo,memo } from "react";
    import { useCallback } from "react";
    
    const Counter = memo(function Counter(props) {
      console.log('Counter render')
      //函数组件可以获取多个context的值,使用方便优雅
      return <h1 onClick={props.onClick}>{props.count}</h1>;
    })
    
    function App() {
      console.log('app render')
      const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return count*2;
      },[count===3])
      const onClick=useCallback(()=>{
        console.log('click');
      },[])
      const half = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return double/4;
      },[double])
      //第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
      return (
        <div>
          <button
            onClick={(a) => {
              setCount(count + 1);
            }}
          >
            Click:({count}) double:({double}) half:({half})
          </button>
    
          <Counter count={double} onClick={onClick}></Counter>
        </div>
      );
    }
    export default App;
    
    

    Ref hooks

    useRef其中的一个使用场景是调用子组件的函数

    import React from "react";
    import { useState,useMemo,memo,PureComponent } from "react";
    import { useCallback,useRef } from "react";
    
    // const Counter = memo(function Counter(props) {
    //   console.log('Counter render')
    //   //函数组件可以获取多个context的值,使用方便优雅
    //   return <h1 onClick={props.onClick}>{props.count}</h1>;
    // })
    class Counter extends PureComponent{
      speak(){
        console.log('now counter is '+this.props.count)
      }
      render(){
        const {props} = this;
        console.log('Counter render')
        return <h1 onClick={props.onClick}>{props.count}</h1>; 
      }
    }
    function App() {
      console.log('app render')
      
      const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      const CounterRef = useRef()
      const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return count*2;
      },[count===3])
      const onClick=useCallback(()=>{
        console.log('click');
        CounterRef.current.speak()
      },[CounterRef])
      const half = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return double/4;
      },[double])
      //第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
      return (
        <div>
          <button
            onClick={(a) => {
              setCount(count + 1);
            }}
          >
            Click:({count}) double:({double}) half:({half})
          </button>
    
          <Counter ref={CounterRef} count={double} onClick={onClick}></Counter>
        </div>
      );
    }
    export default App;
    
    

    useRef的另一个作用是保持一个常量不变,比如定时器的id,原本it会随着app的重新渲染会重新定义it,但是如果使用useRef他就能在app重新渲染时保持同一个句柄

    import React from "react";
    import { useState,useMemo,memo,PureComponent } from "react";
    import { useCallback,useRef } from "react";
    import { useEffect } from "react";
    
    // const Counter = memo(function Counter(props) {
    //   console.log('Counter render')
    //   //函数组件可以获取多个context的值,使用方便优雅
    //   return <h1 onClick={props.onClick}>{props.count}</h1>;
    // })
    class Counter extends PureComponent{
      speak(){
        console.log('now counter is '+this.props.count)
      }
      render(){
        const {props} = this;
        console.log('Counter render')
        return <h1 onClick={props.onClick}>{props.count}</h1>; 
      }
    }
    function App() {
      console.log('app render')
      let it = useRef();
      const [count, setCount] = useState(() => {
        console.log("initial count");
        return 0;
      });
      const CounterRef = useRef()
      const double = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return count*2;
      },[count===3])
      const onClick=useCallback(()=>{
        console.log('click');
        CounterRef.current.speak()
      },[CounterRef])
      const half = useMemo(()=>{ //useMemo会执行函数返回count的两倍
        return double/4;
      },[double])
      useEffect(()=>{
        it.current = setInterval(()=>{setCount(count=>count+1)},1000)
      },[])
      useEffect(()=>{
        if(count>10){
          clearInterval(it.current)
        }
      })
      //第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
      return (
        <div>
          <button
            onClick={(a) => {
              setCount(count + 1);
            }}
          >
            Click:({count}) double:({double}) half:({half})
          </button>
    
          <Counter ref={CounterRef} count={double} onClick={onClick}></Counter>
        </div>
      );
    }
    export default App;
    
    

    自定义hooks

    优点是可复用性强,比如下面的useSize,既可以在app中用,也可以在couter子组件中使用

    import React from "react";
    // eslint-disable-next-line 
    import { useState,useMemo,memo,PureComponent } from "react";
    // eslint-disable-next-line 
    import { useCallback,useRef } from "react";
    // eslint-disable-next-line 
    import { useEffect } from "react";
    function useCount(defaultCount){
      const [count, setCount] = useState(defaultCount);
      const it = useRef()
      useEffect(()=>{
        it.current = setInterval(()=>{
          setCount(count=>count+1)
        },1000)
      },[])
      useEffect(()=>{
        if(count>10){
          clearInterval(it.current)
        }
      })
      return [count,setCount]
    }
    
    function useCounter(count){
      const size = useSize()
      return <h1>{count}sizeWidth{size.width}</h1>; 
    }
    function useSize(){
      const [size,setSize] = useState({
        width:document.documentElement.clientWidth,
        height:document.documentElement.clientHeight
      })
      const onResize = useCallback(()=>{
        setSize({
          width:document.documentElement.clientWidth,
          height:document.documentElement.clientHeight
        })
      },[])
      useEffect(()=>{
        window.addEventListener('resize',onResize,false)
        return ()=>window.removeEventListener('resize',onResize)
      },[])
      return size;
    }
    function App() {
      console.log('app render')
      let [count,setCount]=useCount(0)
      const Counter = useCounter(count)
      const size = useSize();
     //第三次点击count=3结果为true,重新计算 double=6,第一第二次double=0,第四次结果为false又重新计算,结果为8
      return (
        <div>
          <button
            onClick={(a) => {
              setCount(count + 1);
            }}
          >
            Click:({count})
          </button>
            {size.width}x{size.height}
          {Counter}
        </div>
      );
    }
    export default App;
    
    

    起源地下载网 » react hooks基本使用方法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元