react高级用法

ref

ref创建

类组件 - createRef
javascript 复制代码
class ClassRef extends Component {

    constructor(props) {
        // 对于类组件来说,我们一般使用 createRef 来进行创建
        super(props);
        this.eleRef = createRef();
        this.inputRef = createRef();
    };

    handleClick = () => {
        // 我们点一下按钮,我们光标就命中到 input 上
        this.inputRef.current.focus();
        console.log(this.eleRef);
    }

    render () {
        return (<div>
            <div id="this_is_me" ref={this.eleRef}></div>
            <input ref={this.inputRef}  />
            <button onClick={this.handleClick} >Focus</button>
        </div>)
    }
}
函数组件 - useRef
js 复制代码
function FuncRef() {

    const inputRef = useRef(null);

    const handleClick = () => {
        inputRef.current.focus()
    }

  return (
    <div>
        <input ref={inputRef}  />
        <button onClick={handleClick} >Focus</button>
    </div>
  )
}

ref 常见的使用方式

ref 标识

ref 一般可以标识在 hostComponent 宿主元素(首字母小写的,最终会变成基础HTML元素的);

ref转发子组件

ref 本身是不能跨层级捕获和传递的,forwardRef 可以接收父元素的ref信息,转发下去。

我们可以通过forwardRef 进行转发。

javascript 复制代码
{
    render () {
        return (<div>
            <div id="this_is_me" ref={this.eleRef}></div>
            <Child ref={this.childRef} />
            <input ref={this.inputRef}  />
            <button onClick={this.handleClick} >Focus</button>
        </div>)
    }
}

const Child = forwardRef((props, ref) => <div>
    <button ref={ref} id="my_btn">

    </button>
</div>)
expose父组件直接调用子组件的方法

useImperativeHandle

  • p1: ref 接收forwardRef 传递进来的ref
  • createHandle: 返回暴露给父组件的 ref 对象
  • deps: 更新ref对象的依赖
javascript 复制代码
useImperativeHandle(ref, () => ({
   focus, changeValue
}))
javascript 复制代码
function FuncRef() {

    const inputRef = useRef(null);
    const exposeRef = useRef(null);

    const handleClick = () => {
        inputRef.current.focus()
    }

  return (
    <div>
        <input ref={inputRef}  />
        <button onClick={handleClick} >Focus</button>
        <div>
        <FancyInput ref={exposeRef} />
        <button onClick={() => exposeRef.current.focus()}>InputFoucs</button>
        <button onClick={() => exposeRef.current.changeValue("xxx")}>changeValue</button>
        </div>

    </div>
  )
}



// 子组件有个 Modal, Drawer -- visible 
const Input = (props, ref) => {
    const inputRef = useRef();

    const focus = () => {
        inputRef.current.focus()
    };

    const changeValue = (val) => {
        inputRef.current.value = val;
    };

    useImperativeHandle(ref, () => ({
        focus, changeValue
    }))

    return <input ref={inputRef} />
}

const FancyInput = forwardRef(Input);

content

类似 Vue 中的 inject 和 provider

对应过来就是 consumer 和 provider

类用法

js 复制代码
export default class ClassContext extends Component {

    constructor(props) {
        super(props);
        this.state = {
            theme: 'dark'
        }
    }

  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}>
        <Parent />
        <button onClick={() => this.setState({theme: 'light'})}>light</button>
        <button onClick={() => this.setState({theme: 'dark'})}>dark</button>
      </ThemeContext.Provider>
    )
  }
}

const Parent = () => (
    <div>
        <Child1 />
        <Child2 />
    </div>
)

class Child1 extends Component {

    static contextType = ThemeContext;

    render () {
        return <div>
            Child1 -- {this.context}
        </div>
    }
}

class Child2 extends Component {



    render () {
        return <div>
            <ThemeContext.Consumer>
                {
                    (theme) => (
                        <div>
                        Child2 -- {theme}
                        </div>
                    )
                }
            </ThemeContext.Consumer>
        </div>
    }
}

函数用法

js 复制代码
import React, { useContext } from 'react'
import { ThemeContext } from './ThemeContext';

const theme = "blue";

export default function FuncContext() {
  return (
    <ThemeContext.Provider value={theme}>
        <Parent />
    </ThemeContext.Provider>
  )
}

const Parent = () => <Child />


const Child = () => {
    const theme = useContext(ThemeContext);
    return <div>
        <span style={{background: theme}}>Func Usage</span>
    </div>
}
js 复制代码
import React, { useContext } from 'react'
import { ThemeContext } from './ThemeContext';

const theme = "blue";

const navgator = window.history;

export default function FuncContext() {
  return (
    <ThemeContext.Provider value={navgator}>
        <Parent />
    </ThemeContext.Provider>
  )
}

const Parent = () => <Child />

const Child = withNavigator((props) => {
    console.log(props.navgator)
    const theme = useContext(ThemeContext);
    return <div>
        <span style={{background: theme}}>Func Usage</span>
    </div>
})

const withNavigator = (Component) => {
    return function () {
        const navgator = useContext(ThemeContext);
        return <Component navgator={navgator} />
    }
}

HoC高阶组件

属性代理

js 复制代码
import React from 'react'

const withCard = (color) => (Component) => {
    const PrivateCom = (props) => {
        const hocStyle = {
            margin: "4px",
            padding: "4px",
            border:"1px solid #ccc",
            borderRadius: "4px",
            background: color
        }

        return <div style={hocStyle}>
            <Component {...props} hoc={'true'} />
        </div>
    };
    return PrivateCom;
}

const PropsExtends = ( props ) => {
  return (
    <div>P1 -- {props.hoc}</div>
  )
}

export default withCard('blue')(PropsExtends)

反向继承

js 复制代码
import React, { Component } from 'react'

// 比如我们有一个 case , 我们需要非常优雅地实现埋点的曝光

function logProps(logMap) {
    return (WarppedComponent) => {
        const didMount = WarppedComponent.prototype.componentDidMount;
        return class A extends WarppedComponent {
            componentDidMount() {
                if(didMount) {
                    didMount.apply(this);
                }

                Object.entries(logMap).forEach(([k ,v]) => {
                    if(document.getElementById(k)) {
                        console.log(v)
                    }
                })

            }

            render() {
                return super.render();
            }

        }
    }
}

export default function ReverseExtends() {
  return (
    <div>
        <LogIndex />
    </div>
  )
};


class Index extends Component {
    render() {
        return (
            <div>
                <div id="text">
                    这是一个简单的XXX组件
                </div>
            </div>
        )
    }
}
const LogIndex = logProps({ "text": "text_module_show" })(Index)

父组件直接隔离子组件的渲染

类组件
js 复制代码
import React, { Component } from 'react'

export default class RenderControl extends Component {

    constructor(props) {
        super(props);
        this.state = {
            num: 0,
            count: 0
        }

        this.component = <Child num={this.state.num} />
    }

    controllComponentRender = () => {
        const { props } = this.component;
        if(props.num !== this.state.num) {
            return this.component = React.cloneElement(this.component, { num: this.state.num })
        }
        return this.component;
    }

  render() {
    return (
      <div>
        { this.controllComponentRender() }
        <button onClick={() => this.setState({ num: this.state.num + 1 })}>num:{this.state.num}</button>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>count:{this.state.count}</button>
      </div>
    )
  }
}

const Child = ({ num }) => {
    console.log("子组件执行")
    return <div>this is the child {num}</div>
}
函数组件

useMemo 的实现。

js 复制代码
export default function RenderControl() {
 
    const [ num, setNum ] = useState(0);
    const [ count, setCount ] = useState(0);

  return (
    <div>
        {useMemo(() => <Child num={ num} />, [num] )}
        <button onClick={() => setNum(num + 1 )}>num:{ num}</button>
        <button onClick={() => setCount(count + 1 )}>count:{ count}</button>
      
    </div>
  )
}

useMemo

  • 函数: 返回值进行缓存
  • deps: 依赖项,谁变了,我再次执行这个函数。

useCallback

  • 函数:返回值,缓存这个函数
  • deps: 依赖项,谁变了,我再次执行这个函数。
js 复制代码
import React, { Component, useCallback, useMemo, useState } from 'react'


export default function RenderControl() {
 
    const [ num, setNum ] = useState(0);
    const [ count, setCount ] = useState(0);

    const handleClick = useCallback(() => {

    }, [])

    const memoChildNum = useMemo(() => <Child num={num} name={'num'} />, [num] )
    const memoChildCount = useMemo(() => <Child 
        num={ count} 
        name={'count'}
        onClick={handleClick}
          />, [count, handleClick] )

  return (
    <div>
        {memoChildNum}
        {memoChildCount}
        <MemoChild num={count} 
        name={'count'}
        onClick={handleClick} />
        <button onClick={() => setNum(num + 1 )}>num:{ num}</button>
        <button onClick={() => setCount(count + 1 )}>count:{ count}</button>
      
    </div>
  )
}


const Child = ({ num, name }) => {
    console.log("子组件执行" + name)
    return <div>this is the child {name}</div>
}

const MemoChild = React.memo(Child)

组件本身控制要不要额外渲染

  • 生命周期 shouldComponentUpdate
  • PureComponent
    • 对 props 和 state 做一层浅比较
javascript 复制代码
export default class RenderControl extends PureComponent {

    constructor(props) {
        super(props);
        this.state = {
            obj: {
                num: 0,
                count: 0
            }
        }
    }

    handleClick = () => {
        this.state.obj.num = 10
        // setState 触发渲染。
        // PureComponent不更新,因为做的是浅比较。
        this.setState({
            obj: this.state.obj
        })
    }

    

  render() {
    const { num, count } = this.state.obj;
    return (
      <div>
        <div>{num}, {count}</div>
        <button onClick={this.handleClick}>10</button>
      </div>
    )
  }
}
相关推荐
Amore05257 小时前
React+TS前台项目实战(二十三)-- 基于属性自定义数值显示组件Decimal封装
前端·react.js·typescript·前端框架
名字还没想好☜11 小时前
React Hooks --- 分享自己开发中常用的自定义的Hooks (1)
前端·javascript·react.js
LLLuckyGirl~15 小时前
react之错误边界
前端·react.js·前端框架
@PHARAOH19 小时前
WHAT - React useReducer vs Redux
前端·react.js·前端框架
开心小老虎1 天前
react_web自定义组件_多类型Modal_搜索栏Search
前端·react.js·组件
narukeu1 天前
React 中 useState 和 useReducer 的联系和区别
前端·react.js·前端框架
许良辰1 天前
前端第一学习框架--react基础
react.js
黑白小怪兽1 天前
依赖倒置在前端中的典型应用
vue.js·react.js·设计模式
前端达人2 天前
基于React和TypeScript的开源白板项目(Github项目分享)
前端·react.js·typescript·前端框架·github
檀玥2 天前
创建react的脚手架
前端·javascript·react.js