react Hook

react

概念:在不使用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 使得控制具体子节点何时更新变得更容易,减少了对纯组件的需要。

还有一个以纯函数组件的优化方案。那就是React.memo. 他相当于一个高阶组件 class组件优化shouldcomponentupdate  用的是 React.Purcomponent

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);

以上是 react Hook 的全部内容, 来源链接: utcz.com/z/382435.html

回到顶部