最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 写了3个月React,我学到了什么?

    正文概述 掘金(趁你还年轻233)   2021-05-22   453

    原文链接: 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操作,动画,过渡)->样式规则计算->计算空间和位置->绘制像素内容->多个层合成 前四个阶段都是针对元素的,最后一个是针对层的。由点到面。 写了3个月React,我学到了什么?

    执行时间不同

    useEffect在渲染完成后执行函数,更加准确的来说是在layout和paint完成之后。

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

    哪些适合在渲染完成后执行,哪些适合在渲染过程中执行

    渲染完成后执行: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相关的函数)

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

    总结

    useEffect面对一些依赖于某个state的DOM渲染时,会出现一些性能问题,而useMemo可以优化这个问题。 最后,用一句话来概括useMemo的话,那就是:useMemo可以避免一些useEffect搞不定的不必要的重复渲染和重复执行问题。

    React除了可以通过props传递数据以外,如何通过context方式传递数据?

    假设组件层级较深,props需要一级一级往下传,可以说是props hell问题。 context方式封装的组件,为需要接受数据的组件,提供了一种跨组件层级传递,按需引入上级props的方式。

    组件定义context部分

    import * as React from 'react'
    // myContext.ts
    interface 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>
    
    // Component1
    import  {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.js
    import 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.js
    import 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)}</>;
        });
      }
    }
    

    具体的示例可以查看:codesandbox.io/s/react-com…

    基于这种方式,可以封装出可重用的业务组件:表单业务组件,表格业务组件等等,会极大程度的解放生产力!

    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.js
    import 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等等类型约束,纯函数组件没有限定

    stackoverflow.com/questions/5…

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

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

    好文章:fettblog.eu/typescript-…

    • 用于事件类型约束
    • 除了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。


    起源地下载网 » 写了3个月React,我学到了什么?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元