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>
    )
  }
}
相关推荐
中微子1 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
中微子2 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
前端_学习之路3 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_3 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
甜瓜看代码3 小时前
1.
react.js·node.js·angular.js
伍哥的传说3 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
Misha韩5 小时前
React Native 一些API详解
react native·react.js
小李飞飞砖5 小时前
React Native 组件间通信方式详解
javascript·react native·react.js
小李飞飞砖5 小时前
React Native 状态管理方案全面对比
javascript·react native·react.js