• react Hook


    概念:在不使用class组件的情况下,允许你使用state和react的其他特性

    产生背景:在组件之间公用相同的逻辑往往很难,在以前的解决方案是:高阶组件和render props  但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。

    你可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。

    Hooks不要在循环,条件或嵌套函数中以及class组件中调用 Hook,只在最顶层使用hook

    不要在普通的函数中调用hook,只在react函数中调用hook

    import React, { Component,useState } from 'react';
    import ReactDOM from 'react-dom'
    import PropTypes from 'prop-types';
    
    function Example(){
        // count变量的值是useState返回的第一个值  setCount是useState返回的第二个值
      //数组解构 const [count,setCount] = useState(0) return ( <div> <p>you click {count} times</p> <button onClick={()=>setCount(count+1)}>点击我</button> </div> ) }
    ReactDOM.render(<Example />,document.getElementById('root'))

     为避免重新创建被忽略的初始 state,我们可以传一个 函数useState

    useState(()=>0) 这就叫惰性初始化   不会多次初始化只会初始化一次

    useRef不能传递一个函数来初始化 需要自己编写惰性初始化
    function Image(props) {
      const ref = useRef(null);
    
      // ✅ IntersectionObserver 只会被惰性创建一次
      function getObserver() {
        if (ref.current === null) {
          ref.current = new IntersectionObserver(onIntersect);
        }
        return ref.current;
      }
    
      // 当你需要时,调用 getObserver()
      // ...
    }
     
    import React, { Component,useState,useEffect} from 'react';
    import ReactDOM from 'react-dom'
    import PropTypes from 'prop-types';
    
    function Example(){
        const [count,setCount] = useState(0)
        // 每次渲染完毕后都会执行useEffect
        //类似于class组件的  componentDidMount和componentDidUpdate
      //在执行当前 effect 之前对上一个 effect 进行清除
      //useEffect 在浏览器渲染和布局完成之后会执行effect useEffect(()=>{ console.log(123) document.title = `You clicked ${count} times`
        //如果需要清除副作用 则需要返回一个函数来清除副作用,如果不需要清除副作用 则不需要返回一个函数 },[]) //如果第二个参数是一个空数组 useEffect在挂载和卸载的时候只会执行一次
    //React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect,因此会使得额外操作很方便。
    return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count+1)}> Click me </button> </div> ) } //通常在Effect的内部声明他所需要的函数
    //这样就能容易的看出那个 effect 依赖了组件作用域中的哪些值: ReactDOM.render(
    <Example />,document.getElementById('root'))//
    import React, { Component,useState,useEffect} from 'react';
    import ReactDOM from 'react-dom'
    import PropTypes from 'prop-types';
    
    function Counter({initialCount}) {
        const [count, setCount] = useState(initialCount);
        return (
          <React.Fragment>
                Count: {count}
                <button onClick={() => setCount(initialCount)}>Reset</button>
                <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
                <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
                {/*  */}
            </React.Fragment>
        );
      }
      //上面更新状态类似调用下面的 this.setState
      class App extends React.Component{
          constructor(props){
            this.setState((state)=>({
    
            }))
          }
      }
    
    ReactDOM.render(<Counter initialCount={0} />,document.getElementById('root'))
    

     如果你的更新函数返回值与当前 state 完全相同,则随后的重渲染会被完全跳过。

    useEffect的执行时机:等到浏览器完成布局和绘制之后,传给 useEffect 的函数会延迟调用

    默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。销毁前一个effect  创建一个新的effect 

    然而,在某些场景下这么做可能会矫枉过正。比如,在上一章节的订阅示例中,我们不需要在每次组件更新时都创建新的订阅,而是仅需要在 source prop 改变时重新创建。

    要实现这一点,可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组。

    useEffect(
      () => {
        const subscription = props.source.subscribe();
        return () => {
          subscription.unsubscribe();
        };
      },
      [props.source],
    );
    

     useReducer

    import React, { Component,PropTypes,useContext,useReducer } from 'react';
    import ReactDOM from 'react-dom'
    import { ThemeContext,themes } from './components/ThemeContext';
    // import ThemeButton from './components/ThemeButton'
    const initialState = {count: 0};
    function reducer(state,action){
        switch(action.type){
            case 'increment':
                return {count:state.count+1}
            case 'decrement':
                return {count:state.count-1}
            default:
                throw new Error()
        }
    }
    
    function Counter(){
        const [state,dispatch] = useReducer(reducer,initialState)
        return (
            <React.Fragment>
                counter:{state.count}
                <button onClick={()=>dispatch({type:'decrement'})}>-</button>
                <button onClick={()=>dispatch({type:'increment'})}>+</button>
            </React.Fragment>
        )
    }
    
    
     
    ReactDOM.render(<Counter />,document.getElementById('root'))
    
    import React, { Component,PropTypes,useContext,useReducer } from 'react';
    import ReactDOM from 'react-dom'
    import { ThemeContext,themes } from './components/ThemeContext';
    // import ThemeButton from './components/ThemeButton'
    //懒初始状态
    function init(initialState){
        return {
            count:initialState
        }
    }
    // const initialState = {count: 0};
    function reducer(state,action){
        switch(action.type){
            case 'increment':
                return {count:state.count+1}
            case 'reset':
                return init(action.payload)
            case 'decrement':
                return {count:state.count-1}
            default:
                throw new Error()
        }
    }
    
    function Counter({initialState}){
        console.log(useReducer(reducer,initialState,init))
        const [state,dispatch] = useReducer(reducer,initialState,init)
        return (
            <React.Fragment>
                counter:{state.count}
                <button onClick={()=>dispatch({type:'reset','payload':initialState})}>reset</button>
                <button onClick={()=>dispatch({type:'decrement'})}>-</button>
                <button onClick={()=>dispatch({type:'increment'})}>+</button>
            </React.Fragment>
        )
    //跳过 dispatch
    //如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行。(React 使用 Object.is 比较算法 来比较 state。)
    } ReactDOM.render(<Counter initialState={0} />,document.getElementById('root'))
    import React, { Component,PropTypes,useContext,useReducer ,useEffect,useRef,useImperativeHandle} from 'react';
    import ReactDOM from 'react-dom'
    import { ThemeContext,themes } from './components/ThemeContext';
    // import ThemeButton from './components/ThemeButton'
    
    function FancyInput(props,ref){
        const input = useRef();
        useImperativeHandle(ref,()=>({
            focus:()=>{
                input.current.focus();
            }
        }))
        return <input ref={input} {...props} />
    }
    FancyInput = React.forwardRef(FancyInput)
    class App extends React.Component{
        constructor(props){
            super(props)
            this.inputRef = React.createRef()
        }
        componentDidMount() {
            this.inputRef.current.focus()
        }
        render(){
            return (
                <FancyInput ref={this.inputRef} />
            )
        }
    }
    ReactDOM.render(<App />,document.getElementById('root'))
    

     利用hook对数据进行获取

    import React, { useState, useEffect } from "react";
    import ReactDOM from "react-dom";
    import axios from 'axios';
    
    function SearchResults(){
        const [data,setData] = useState({hits:[]})
        const [query,setQuery] = useState("react")
        // await axios('https://hn.algolia.com/api/v1/search?query=' + query);
        useEffect(()=>{
            let ignore = false;
            async function fetchData(){
               let result = await axios('https://hn.algolia.com/api/v1/search?query=' + query);
               console.log(3)
              if(!ignore) {
                console.log('1')
                setData(result.data)
              }
            }
            fetchData()
            return ()=>{
                console.log('2')
                ignore = true;
            }
        },[query])
        return (
            <React.Fragment>
                <input value={query} onChange={(e)=>setQuery(e.target.value)} />
                <ul>
                {data.hits.map((item)=>(
                    <li key={item.objectID}>
                        <a href={item.url}>{item.title}</a>
                    </li>
                ))}
                    
                </ul>
            </React.Fragment>
        )
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<SearchResults />, rootElement);
    

     获取前一次的props或state

    import React, { useState, useEffect,useRef } from "react";
    import ReactDOM from "react-dom";
    import axios from 'axios';
    
    function Counter() {
        const [count, setCount] = useState(0);
        const prevCount = usePrevious(count); 
        return <h1>Now: {count}, before: {prevCount} <button onClick={()=>setCount(count+1)}>+</button></h1>;
      }
      
      function usePrevious(value) {  
        const ref = useRef();
        useEffect(() => {
          ref.current = value;
        });
        return ref.current;
      }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<Counter />, rootElement);

     useCallback的应用场景

    useCallback Hook 允许你在重新渲染之间保持对相同的回调引用以使得 shouldComponentUpdate 继续工作:

    useMemo Hook 使得控制具体子节点何时更新变得更容易,减少了对纯组件的需要。

    useReducer Hook 减少了对深层传递回调的依赖

    import React, { useState, useEffect,useRef,useCallback } from "react";
    import ReactDOM from "react-dom";
    import axios from 'axios';
    
    //每当ref被添加到另外一个节点 react就会调用callback
    
    function App(){
      const [height,setHeight] = useState(0)
      const measuredRef = useCallback(node=>{
        if(node!=null){
          setHeight(node.getBoundingClientRect().height)
        }
      })
      return (
        <React.Fragment>
          <h1 ref={measuredRef}>Hello, world</h1>
          <h1>The above header is {Math.round(height)}px tall</h1>
        </React.Fragment>
      )
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
  • 相关阅读:
    Vue路由机制
    谷歌浏览器打不开应用商店的解决方法
    Vue报错——Component template should contain exactly one root element. If you are using vif on multiple elements, use velseif to chain them instead.
    Vue.js学习之——安装
    Vue使用axios无法读取data的解决办法
    关于localstorage存储JSON对象的问题
    2013年整体计划
    个人喜欢的警语收集
    Linux防火墙的关闭和开启
    Flex修改title 转载
  • 原文地址:https://www.cnblogs.com/nianzhilian/p/13365446.html
Copyright © 2020-2023  润新知