zoukankan      html  css  js  c++  java
  • 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);
    
  • 相关阅读:
    JNDI----数据连接池
    转发和重定向的区别
    vs编译程序不能实现,“未能完成操作 未指定的错误”的解决办法
    error C1853: “DebugBigBuffer.pch”预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
    monkey测试结果分析
    VSpy之C Code Interface的使用
    C++常备知识总结
    关于“调试会话的可执行文件”对话框
    The JVM found at JAVA_HOME is damaged.Please reinstall or define EXE4J_JAVA_HOME to point to an installed 32-bit JDK or JRE
    exe4j中"this executable was created with an evaluation version of exe4j"
  • 原文地址:https://www.cnblogs.com/nianzhilian/p/13365446.html
Copyright © 2011-2022 走看看