原文链接:
React那些事儿
React hooks那些事儿

新环境从Vue转到了React技术栈,这个过程还是比拟乏味的。

在React中会看到与Vue很多类似的中央,也有一些不同的中央,学习过程中遇到一些纳闷,做了记录。

  • useRef如何解决空指针问题?
  • useEffect与useCallback(useMemo)的区别是什么?
  • React除了能够通过props传递数据以外,如何通过context形式传递数据?
  • React.createElement(Input, props)中的React.createElement如何了解?
  • react中的FC是什么?FC<[interface]>是什么意思?次要用途及最简写法是怎么的?
  • React中FC的形参的props, context, propTypes, contextTypes, defaultProps, displayName是什么?
  • import { MouseEvent } from 'react'是什么意思?SyntheticEvent是什么类型?
  • React.forwardRef是什么意思?useImperativeHandle是什么意思?

useRef如何解决空指针问题?

通常来说,useRef用于援用组件的Dom节点。Vue中的ref则是援用一个vue组件。与Vue不同,react中的ref不仅仅是援用Dom节点,还能够生成一个内存不变的对象援用。

应用useState导致的空指针示例

const [foo, setFoo] = useState(null);const handler = () => {    setFoo("hello")}useEffect(() => {    return () => {      // 无论怎样foo都是null,给useEffect的deps退出foo也不行      if (foo === "hello") {          // do something...      }    }}, [])

应用useRef的正确示例(解决事件处理器中对象为null的问题)

const foo = useRef(null)const handler = () => {    foo.current = "hello"}useEffect(() => {    return () => {      // foo.current为hello      if (foo.current === "hello") {          // do something...      }    }}, [])

useRef解决空指针问题的起因是什么?

  • 组件生命周期期间,useRef指向的对象都是始终存在的
  • 每次渲染时,useRef都指向同一个援用的对象

总结起来就是:useRef生成的对象,在组件生命周期期间内存地址都是不变的。

const refContainer = useRef(initialValue);

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.

总结一下会应用到useRef解决空指针问题的场景:

  • 事件处理器
  • setTimeout,setInterval

useEffect与useCallback(useMemo)的区别是什么?

浏览器执行阶段:可见批改(DOM操作,动画,过渡)->款式规定计算->计算空间和地位->绘制像素内容->多个层合成
前四个阶段都是针对元素的,最初一个是针对层的。由点到面。

执行工夫不同

useEffect在渲染实现后执行函数,更加精确的来说是在layout和paint实现之后。

The function passed to useEffect will run after the render is committed to the screen.Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint

useCallback(useMemo)在渲染过程中执行函数。

Remember that the function passed to useMemo runs during rendering.

哪些适宜在渲染实现后执行,哪些适宜在渲染过程中执行

渲染实现后执行:Mutations(DOM操作), subscriptions(订阅), timers, logging
渲染过程中执行:用于不依赖渲染实现的性能优化,状态一变更立刻执行

一个例子说明useEffect和useMemo的区别

useMemo最次要解决的问题:怎么在DOM扭转的时候,管制某些函数不被触发。
例如上面这个例子,在name变更的时候,useEffect会在DOM渲染实现后登程price的函数,而useMemo能够精准的只触发更新name的函数。

这是一个十分十分好的例子,更加具体的博文在这里:useMemo和useEffect有什么区别?怎么应用useMemo

import React, {Fragment} from 'react'import { useState, useEffect, useCallback, useMemo } from 'react'const nameList = ['apple', 'peer', 'banana', 'lemon']const Example = (props) => {    const [price, setPrice] = useState(0)    const [name, setName] = useState('apple')            function getProductName() {        console.log('getProductName触发')        return name    }    // 只对name响应    useEffect(() => {        console.log('name effect 触发')        getProductName()    }, [name])        // 只对price响应    useEffect(() => {        console.log('price effect 触发')    }, [price])      // memo化的getProductName函数       const memo_getProductName = useMemo(() => {        console.log('name memo 触发')        return () => name  // 返回一个函数    }, [name])    return (        <Fragment>            <p>{name}</p>            <p>{price}</p>            <p>一般的name:{getProductName()}</p>            <p>memo化的:{memo_getProductName ()}</p>            <button onClick={() => setPrice(price+1)}>价格+1</button>            <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>批改名字</button>        </Fragment>    )}export default Example

点击价格+1按钮(通过useMemo,多余的memo_getProductName ()没有被触发,只触发price相干的函数)

getProductName触发
price effect 触发

点击批改名字按钮(通过useEffect,只触发name相干)

name memo 触发
getProductName触发
name effect 触发
getProductName触发
总结

useEffect面对一些依赖于某个state的DOM渲染时,会呈现一些性能问题,而useMemo能够优化这个问题。
最初,用一句话来概括useMemo的话,那就是:useMemo能够防止一些useEffect搞不定的不必要的反复渲染和反复执行问题。

React除了能够通过props传递数据以外,如何通过context形式传递数据?

假如组件层级较深,props须要一级一级往下传,能够说是props hell问题。
context形式封装的组件,为须要承受数据的组件,提供了一种跨组件层级传递,按需引入下级props的形式。

组件定义context局部

import * as React from 'react'// myContext.tsinterface IContext {     foo: string,     bar?: number,     baz: string}const myContext = React.createContext<IContext>({     foo: "a",     baz: "b"})interface IProps {    data: IContext ,}const myProvider: React.FC<IProps> = (props) => {     const {data, children} = props     return <myContext.Provider value={data}>{children}</myContext.Provider>}export default myProvider;export function useMyContext() {  return useContext(myContext)}

应用组件和context局部

<!-- 组件包裹 -->import myProvider from './myContext.ts'<myProvider data={{foo: "foo", baz: "baz"}}>    <div className="root">        <div className="parent">            <Component1 />            <Component2 />        </div>     </div></myProvider>
// Component1import  {useMyContext} from './myContext.ts'const {foo, baz} = useMyContext()const Compoonent1 = () => {    return (<div>{foo}{baz}</div>)}export Component1

React.createElement(Input, props)中的React.createElement如何了解?

React.createElement()

React.createElement(    type,    [props],    [...children])

依据指定类型,返回一个新的React element。

类型这个参数能够是:

  • 一个“标签名字符串”(例如“div”,“span”)
  • 一个React component 类型(一个class或者一个function)
  • 一个React fragment 类型

JSX写法的组件,最终也会被解析为React.createElement()的形式。如果应用JSX的形式的话,不须要显式调用React.createElement()。

React.createElement(Input, props)

基于antd,封装通用表单组件办法。

// generator.jsimport React from "react";import { Input, Select } from "antd";const components = {  input: Input,  select: Select};export default function generateComponent(type, props) {  return React.createElement(components[type], props);}

简略应用这个通用表单组件办法:

import generateComponent from './generator'const inputComponent = generateComponent('input', props)const selectComponent = generateComponent('select', props)

你可能会感觉下面这种形式比拟鸡肋,然而如果批量地生成组件,这种形式就很有用了。

// components.jsimport React from "react";import generateComponent from "./generator";const componentsInfos = [  {    type: "input",    disabled: true,    defaultValue: "foo"  },  {    type: "select",    autoClear: true,    dropdownStyle: { color: "red" }  }];export default class Components extends React.Component {  render() {    return componentsInfos.map((item) => {      const { type, ...props } = item;      return <>{generateComponent(type, props)}</>;    });  }}

具体的示例能够查看:https://codesandbox.io/s/reac...

基于这种形式,能够封装出可重用的业务组件:表单业务组件,表格业务组件等等,会极大水平的解放生产力!

react中的FC是什么?FC<[interface]>是什么意思?次要用途及最简写法是怎么的?

react中的FC是什么?

type FC<P = {}> = FunctionComponent<P>;
interface FunctionComponent<P = {}> {    (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;    propTypes?: WeakValidationMap<P>;    contextTypes?: ValidationMap<any>;    defaultProps?: Partial<P>;    displayName?: string;}

FC是FunctionComponent的缩写,FunctionComponent是一个泛型接口。

FC<[interface]>是什么意思?

是为了提供一个函数式组件环境,用于包裹组件。
为什么呢?因为在函数式组件外部能够应用hooks。

函数式组件

const Component = (props) => {    // 这里能够应用hooks    return <div />}或者function Component(props) {  // 这里能够应用hooks  return <div />;}

次要用途及最简写法是怎么的?

我的项目内的公共函数式组件,作为组件容器应用,用于提供hooks上下文环境。

// Container.jsimport React, { FC } from 'react'interface IProps {     children: any}const Container: FC<IProps> = (props) =>  {  return (    <div>      {props.children}    </div>  )}export default Container
// 应用<Container>    <Component1 />    <Component2 /></Container>

React中FC的形参的props, context, propTypes, contextTypes, defaultProps, displayName是什么?

type FC<P = {}> = FunctionComponent<P>;interface FunctionComponent<P = {}> {        (props: PropsWithChildren<P>, context?: any): ReactElement | null;        propTypes?: WeakValidationMap<P>;        contextTypes?: ValidationMap<any>;        defaultProps?: Partial<P>;        displayName?: string;}type PropsWithChildren<P> = P & { children?: ReactNode };

其中props和context都是函数组件的形参。
而propTypes,contextTypes,defaultProps,displayName都是组件的函数组件的属性。

const Foo: FC<{}> = (props, context) => {    return (        <div>{props.children}</div>    )}Foo.propTypes = ...Foo.contextTypes = ...Foo.defaultProps = ...Foo.displayName = ...

react函数式组件与纯函数组件有什么区别呢?

1.react函数式组件必须返回ReactElement或者null,纯函数组件返回值没有限定
2.react函数式组件的props限定children的类型为ReactNode,纯函数组件没有限定
3.react函数式组件领有propTypes,contextTypes,defaultProps,displayName等等类型束缚,纯函数组件没有限定

https://stackoverflow.com/que...

import { MouseEvent } from 'react'是什么意思?SyntheticEvent是什么类型?

import { MouseEvent } from 'react'是什么意思?

好文章:https://fettblog.eu/typescrip...

  • 用于事件类型束缚
  • 除了MouseEvent,还有AnimationEvent, ChangeEvent, ClipboardEvent, CompositionEvent, DragEvent, FocusEvent, FormEvent, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent, TransitionEvent, WheelEvent. As well as SyntheticEvent
  • 能够应用MouseEvent<HTMLButtonElement>束缚仅触发HTML button DOM的事件
  • InputEvent较为非凡,因为是一个试验事件,因而能够用SyntheticEvent代替

SyntheticEvent是什么类型?

Synthetic -> 合成的

在React中,简直所有的事件都继承了SyntheticEvent这个interface。
SyntheticEvent是一个跨浏览器的浏览器事件wrapper,通常用于代替InpuEvent这样的事件类型。

interface SyntheticEvent<T = Element, E = Event> extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
interface BaseSyntheticEvent<E = object, C = any, T = any> {    nativeEvent: E;    currentTarget: C;    target: T;    bubbles: boolean;    cancelable: boolean;    defaultPrevented: boolean;    eventPhase: number;    isTrusted: boolean;    preventDefault(): void;    isDefaultPrevented(): boolean;    stopPropagation(): void;    isPropagationStopped(): boolean;    persist(): void;    timeStamp: number;    type: string;}

React.forwardRef是什么意思?useImperativeHandle是什么意思?

简而言之,refs转发就是为了获取到组件外部的DOM节点。
React.forwardRef意思是Refs转发,次要用于将ref主动通过组件传递到某一子组件,常见于可重用的组件库中。

在应用forwardRef时,能够让某些组件接管ref,并且将其向下传递给子组件,也能够说是”转发“给子组件。

没有应用refs转发的组件。

function FancyButton(props) {  return (    <button className="FancyButton">      {props.children}    </button>  );}

应用refs转发的组件。

const FancyButton = React.forwardRef((props, ref)=>{  <button ref={ref} className="FancyButton">    {props.children}  </button>})

如何应用?

// 创立一个ref变量const ref = React.createRef();// 将ref变量传入FancyButton,FancyButton将ref变量转发给button<FancyButton ref={ref}></FancyButton>// ref.current指向button DOM节点

vue中也有refs机制不同,然而vue如果想获取到子组件外部的DOM节点,须要一级一级的去获取,比方this.$refs.parent.$refs.child,这会导致组件层级依赖重大。
相比vue而言,React的refs转发组件层级以来较轻,代码可读性和可维护性更高。

useImperativeHandle是什么意思?

import React, { useRef, useImperativeHandle } from 'react';import ReactDOM from 'react-dom';const FancyInput = React.forwardRef((props, ref) => {  const inputRef = useRef();  useImperativeHandle(ref, () => ({    publicFocus: () => {      inputRef.current.focus();    }  }));  return <input ref={inputRef} type="text" />});const App = props => {  const fancyInputRef = useRef();  return (    <div>      <FancyInput ref={fancyInputRef} />      <button        onClick={() => fancyInputRef.current.publicFocus()}      >父组件调用子组件的 focus</button>    </div>  )}ReactDOM.render(<App />, root);

下面这个例子中与间接转发 ref 不同,间接转发 ref 是将 React.forwardRef 中函数上的 ref 参数间接利用在了返回元素的 ref 属性上,其实父、子组件援用的是同一个 ref 的 current 对象,官网不倡议应用这样的 ref 透传,而应用 useImperativeHandle 后,能够让父、子组件别离有本人的 ref,通过 React.forwardRef 将父组件的 ref 透传过来,通过 useImperativeHandle 办法来自定义凋谢给父组件的 current。

期待和大家交换,共同进步:

  • 微信公众号: 大大大前端 / excellent_developers
  • Github博客: 趁你还年老233的集体博客
  • SegmentFault专栏:趁你还年老,做个优良的前端工程师
致力成为优良前端工程师!