这里的所有内容均摘自网上优秀的文章,或再加工,只供自己学习用,如有侵权,请联系。会在以后的使用过程中不断补充,修改。
React Hook 概述
什么是 Hook:
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
这篇文章有什么:
这里不过多阐述使用 Hook 的动机,网上都有,如果一定要用 Hook ,这片文章将收集,初次使用 Hook ,所需要知道的干货。
Hook 知识点:
useState | useEffect | useContext | useReducer | useCallback | useMemo | useRef | useHistory
(打√的为我用过的,也是比较常用的,其它有机会再用)
useState√
import React, { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
const [state, setState] = useState(initialState);
useState:主要是为了给函数添加状态的。最好(必须)在函数的最外层申明使用,不要在if或者else里面使用。
initialState:是 state 的初始值,不限制数据类型,不写默认 undefined。
setState:是改变 state 的方法,类似于原来的 setState({state:false }),区别是没有钩子函数,也就是不能这样- -> setState({state:false }, () => { console.log(state) }) 操作。 setState 函数用于更新 state,它接收一个新的 state 值并将组件的一次重新渲染加入队列(注意:可以在 render 中使用 setState ,不会重复触发 render)。
在后续的重新渲染中,useState
返回的第一个值将始终是更新后最新的 state。
useEffect √
useEffect的作用是依赖变化的时候,执行函数(第一个参数),其中第二个参数为依赖。
第二个参数的作用:
虽然 useEffect
会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。React 将在组件更新前刷新上一轮渲染的 effect。
不写“依赖”,可能会导致没必要的刷新,甚至无限刷新。
写上第二个参数,effect 会监测“依赖”是否变化,当“依赖”变化时才会刷新。
若依赖是一个空数组,effect 会判定依赖没有更新,所以只会执行一次。
effect 第一次被执行的生命周期:第一次 render(创建 DOM),执行 effect ,第二次 render,...
例子1:
function App() { const [width,setWidth] = useState(window.innerWidth)
useEffect(()=>{ const handleResize = () => setWidth(window.innerWidth) window.addEventListener('resize',handleResize) return ()=>{ // 清除订阅、定时器、等 window.removeEventListener('resize',handleResize) } }, [])
return ( <div> <p>{width}</p> </div> ) }
useContext
之前我们使用context (上下文)来解决多层嵌套传props,分三步
- createContext创建Context
- 使用Context.Provider组件提供数据
- Context.Provider的所有后代组件,都可以通过Context.Consumer使用数据数据
const ColorContext = React.createContext('black') function Button(props){ return ( <ColorContext.Consumer> {color=> <button style={{ background: color }}> {color,props.children} </button>} </ColorContext.Consumer> ); } function MiddleWare(){ return ( <Button>我来了</Button> ) } function App() { return ( <div> <ColorContext.Provider value='yellow'> <MiddleWare></MiddleWare> </ColorContext.Provider> </div> ); }
useContext 例子:
如果需要在组件之间共享状态(比如两个有共同父级的子组件之间),可以使用useContext()。
一层的数据传递,感觉没有必用 useContext,如果是多层嵌套的话,父传子的方式就显得很不好了,此时应该用到它。
需要用到状态的子组件:
import React, { useContext } from "react";
function Button2(props){ const color = useContext(ColorContext) return <button style={{ background: color }}>{(color, props.children)}</button> }
提供状态的父组件
import React from "react";
const TestContext= React.createContext({});
function MiddleWare(props) { return <ColorContext.Provider value="yellow"> <Button2>很多个子组件的其中一个</Button2> </ColorContext.Provider> }
useReducer
让我们来回忆一下 使用redux使用reducer
//1.首先创建一个store index.store.js export default function configStore(){ const store = createStore(rootReducer,applyMiddleware(...middlewares)) return store } //2.引入store app.js render() { return ( <Provider store={store}> <Index /> </Provider> ) } //3.定义action和创建reducder index.action.js index.reducer.js export const ADD = 'ADD' export const DELETE = 'DELETE' function todos(state = INITAL_STATE, action) { switch action.type{ case ADD:{...} case DELETE:{...} } } //4.页面中使用reducer component.js export default connect(mapStateToProps, mapDispatchToProps)(Component);
太复杂了有没有,(使用dva可以简化写法)
而使用useReducer可以省略很多代码:
//index.js const { state, dispatch } = useContext(reducerContext); return ( <div className="App"> <> Count: {state.count} <button onClick={() => dispatch({ type: "reset", payload: { count: 0 } })} >Reset</button> <button onClick={() => dispatch({ type: "increment" })}>+</button> <button onClick={() => dispatch({ type: "decrement" })}>-</button> <reducerContext.Provider value={{ state, dispatch }}> <ChangeCount /> </reducerContext.Provider> </> </div> );
但是不知是否有像查看 store 的浏览器插件,或者 redux-logger 这样的中间件帮助我们查看状态的变化,redux 的生态还是更好一点的
useCallback
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
返回一个 memoized 回调函数。
把内联回调函数及依赖项数组作为参数传入 useCallback
,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子组件时,它将非常有用。
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
注意:
依赖项数组不会作为参数传给回调函数。虽然从概念上来说它表现为:所有回调函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。
我们推荐启用 eslint-plugin-react-hooks
中的 exhaustive-deps
规则。此规则会在添加错误依赖时发出警告并给出修复建议。
useMemo
useMemo
是什么呢,它跟 memo
有关系吗, memo
的具体内容可以查看 React 中性能优化、 memo、PureComponent、shouldComponentUpdate 的使用,说白了 memo
就是函数组件的 PureComponent
,用来做性能优化的手段,useMemo
也是,useMemo
在我的印象中和 Vue
的computed
计算属性类似,都是根据依赖的值计算出结果,当依赖的值未发生改变的时候,不触发状态改变,useMemo
具体如何使用呢,看下面例子
function App () { const [ count, setCount ] = useState(0) const add = useMemo(() => { return count + 1 }, [count]) return ( <div> 点击次数: { count } <br/> 次数加一: { add } <button onClick={() => { setCount(count + 1)}}>点我</button> </div> ) }
import React, {memo, useMemo} from "react"; //...不清楚 useMemo 怎么用,以后补充 export default memo(xxxx) //导出的时候包一下
useRef √
- useRef 返回一个可变的ref对象-----书写更加简洁
- 返回的ref对象在组件的整个生命周期内保持不变
- ref对象的.current属性设置为相应的DOM节点---------和之前的ref一样的
- 当ref对象内容发生变化时,useRef并不会通知你--------只是一个记录值,不会帮你监听什么
- 变更.current属性不会引发组件重新渲染--------只是记录值,并不会监听数据改变和重新渲染操作等
例子
import React, {useRef,useEffect } from 'react' function A(){ const inputR=useRef() useEffect(()=>{ //页面渲染完成的时候执行 inputR.current.focus() },[]) render() { return ( <div> <input type="text" ref={inputR} /> 定义属性 </div> ) }
useHistory √
react.js-Hooks 路由跳转import { useHistory } from "react-router-dom"; function HomeButton() { let history = useHistory(); function handleClick() { history.push("/home"); } return ( <button type="button" onClick={handleClick}> Go home </button> ); }
开发中遇到的问题与解决
import BtnGroup from "componten/BtnGroup/BtnGroup"; function Father() { const [defaultValue] = useState([ {name: "全部", key: 0, isAll: true, disabled: false}, {name: "中国人", key: 1, disabled: false}, {name: "米国人", key: 2, disabled: false} ]); const click = () => { //... } return ( <div> <BtnGroup> defaultValue={[defaultValue[0]]} handleClick={click} </BtnGroup> </div> ); } export default memo(Father) // 下面是子组件的部分代码,我在子组件中使用 useEffect 监测 props.defaultValue useEffect(() => { console.log("在这个做一些事,设置组件的初始状态") },[props.defaultValue])
细心的朋友可能发现了,在 Father 组件每次触发 render 的时候,都会触发子组件的 useEffect ,应为组件的 useEffect 监测了 props.defaultValue,
这时,又有人问了,我给子组件传的 defaultValue 是不会改变的,如何触发了 子组件的 useEffect ?
注意:我给子组件传 defaultValue[0] 的时候,在外面有包了一层 [ ] , 结果是--> [defaultValue[0]] ,也就是说,每次触发 Father 的 render,
子组件的 defaultValue 都会拿到一个新地址的 [ ],同理,只要在 function Father() { } 的函数体内定义的 [ ] (对象) 的指针,都在状态改变的时候指向新的作用域,
从而被 子组件的 useEffect 监测到,触发不必要的刷新。
解决:1. 不要监测 props.defaultValue,给一个[ ](空数组)。
2. 在函数 function Father() { } 体外申明一个变量 defaultValue = [{name: "全部", key: 0, isAll: true, disabled: false}]
作者:趣谈前端_徐小夕
链接:https://www.jianshu.com/p/16bef85ebd30
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
链接:https://www.jianshu.com/p/7bc5eb7eaaac
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
useHistory