|

Aimee

Write the Code. Change the World.

React API 使用demo

· 分享镜#react

https://github.com/Aimee1608/react-api

纯函数组件

function Component(){
	return (<div>hello world</div>)
}

类组件

import React from 'react'
class Component extends React.Component{
	render(){
  	return (<div>hello world</div>)
  }
}

常规父子组件

// 子组件
import React from 'react'

export default class Child extends React.Component {
  render() {
    const { count } = this.props
    return (
      <div>
        <h2>child</h2>
        <div>count: {count}</div>
      </div>
    )
  }
}


// 父组件
import React from 'react'
import Child from './../Child'
export default class Parent extends React.Component {
  state = {
    count: 0
  }
  handleClick() {
    this.setState({ count: this.state.count + 1 })
  }
  render() {
    const { count } = this.state
    return (
      <div>
        <h1>Parent</h1>
        <button onClick={this.handleClick.bind(this)}>加1</button>
        <Child count={count}></Child>
      </div>
    )
  }
}


生命周期

一个组件的生命周期:

挂载时:
  • constructor
  • static getDerviedStateFromProps
    • 接受两个参数 props 和state ,用于处理state中依赖props的值更新,需要返回内容,默认可以返回null
  • render
  • componetDidMount

更新时:
  • static getDerviedStateFromProps
  • shouldComponentUpdate
    • 接受两个参数,nextProps, nextState 根据参数判断是否需要更新数据,需返回boolean false为不更新,true为更新
  • render
  • getSnapshotBeforeUpdate
    • 接受两个参数 preProps preState 需要返回参数,如果返回null 或者传递给ComponentDidUpdate的值
    • 可以在此钩子中获取真实dom更新前的数据信息,比如滚动的位置
  • componentDidUpdate
    • 接受三个参数,preProps, preState, snapshot, snapshot参数为getSnapshotBeforeUpdate返回的数据

卸载时:
  • componentWillUnmount

错误捕获:
  • static getDerviedStateFromError
    • 接受一个参数,error 用于返回错误信息
    • 如果有副作用需要使用componentDidCatch
    • 会捕获子组件中的错误
  • componentDidCatch
    • 接受两个参数,error 和 info 用于记录错误信息
    • 错误日志捕获这可以在这里设置
    • 会捕获子组件中的错误

// parent
import React from 'react'

export default class Parent extends React.Component {
  state = {
    hasError: false
  }
  //   static getDerviedStateFromError(error) {
  //     console.log('getDerviedStateFromError', error)
  //     return { hasError: true }
  //   }
  componentDidCatch(error, info) {
    console.log('componentDidCatch', error, info)
    this.setState({ hasError: true })
  }
  render() {
    const { hasError } = this.state
    if (hasError) {
      // 你可以渲染任何自定义的降级  UI
      return <h1>Something went wrong.</h1>
    }
    return this.props.children
  }
}



// child
import React from 'react'
export default class Child extends React.Component {
  state = {
    error: null
  }
  handleClick() {
    this.setState({
      error: new Error('error')
    })
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>抛出错误</button>
        {this.state.error}
      </div>
    )
  }
}



// middle
import React from 'react'
import Parent from './Parent'
import Child from './Child'
export default class Middle extends React.Component {
  render() {
    return (
      <Parent>
        <Child></Child>
      </Parent>
    )
  }
}

父子组件生命周期:

子组件中引用了父组件的数据 父组件更新 父组件卸载

挂载时
  • parent constructor
  • parent static getDerviedStateFromProp
  • parent render
  • child constructor
  • child static getDerviedStateFromProp
  • child render
  • child componentDidMount
  • parent componentDidMount

更新时:
  • parent static getDerviedStateFromProps
  • parent shouldComponentUpdate
  • parent render
  • child static getDerviedStateFromProps
  • child shouldComponentUpdate
  • child render
  • child getSnapshotBeforeUpdate
  • parent getSnapshotBeforeUpdate
  • child componentDidUpdate
  • parent componentDidUpdate
卸载时:
  • child componentWillUnmount
  • parent componentWillUnmount

高阶组件

  • 定义一个函数HigherOrderComponent,接受一个或多个参数,其中包含Component组件类
  • 在render中 return Component的信息,组件上可以挂载props数据 和自定义的数据
  • 在Demo中 通过调用HigherOrderComponent 函数就可以得到一个已经注入props的组件

// HigherOrderComponent
import React from 'react'

export default function HigherOrderComponent(Component) {
  return class extends React.Component {
    render() {
      return <Component {...this.props} name="hight"></Component>
    }
  }
}



// Demo
import React from 'react'
import HigherOrderComponent from './HigherOrderComponent'
class Demo extends React.Component {
  render() {
    const { name, count } = this.props
    return (
      <div>
        高阶组件返回的信息:{name} {count}
      </div>
    )
  }
}
export default HigherOrderComponent(Demo)



// Parent
import Demo from './Demo'
import React from 'react'

export default class Index extends React.Component {
  render() {
    return <Demo count={5}></Demo>
  }
}

context上下文

主要解决层级嵌套太深的props传递

简易使用

  • 由React.createContext()创建 context
  • context 中包含Consumer 和Provider
    • Provider为提供方 有一个value属性 接受的是需要提供的数据
    • Consumer是 接受使用方,接受一个函数,函数参数为value
  • 定义自己的组件直接使用Context
import React from 'react'
const Context = React.createContext()

class Button extends React.Component {
  render() {
    return <Context.Consumer>{value => <div>context-info:{value}</div>}</Context.Consumer>
  }
}

export default class Container extends React.Component {
  render() {
    return (
      <Context.Provider value="contxt">
        <Button></Button>
      </Context.Provider>
    )
  }
}

封装通用版的context

  • 创建context
  • 创建connectStore函数,返回一个新的组件,组装Consumer 将Context 的值注入到Component上
  • 常规组件如果需要使用context 只需要,调用connectStore 返回一个新的Component
  • 在根父组件注入Provider,无论多少层子组件中如果需要使用context的,可以通过connectStore来获取
import React from 'react'
// 创建context
const context = React.createContext()
// 生成Consumer 供使用方使用
const Consumer = context.Consumer

const Provider = context.Provider
// 组装Consumer 将Context 的值注入到Component上
function connectStore(Component) {
  return class extends React.Component {
    render() {
      return <Consumer>{value => <Component {...value} {...this.props}></Component>}</Consumer>
    }
  }
}
// 常规的组件定义
class Demo extends React.Component {
  render() {
    const { contextInfo, propsInfo } = this.props
    return (
      <div>
        demo1--顶层-connectStore拿到的信息:{contextInfo}; 父组件拿到的Info:{propsInfo}
      </div>
    )
  }
}
// 生成组装后的组件
const NewDemo = connectStore(Demo)

class Demo2 extends React.Component {
  render() {
    const { contextInfo, propsInfo } = this.props
    return (
      <div>
        demo2--嵌套多层-connectStore拿到的信息:{contextInfo}; 父组件拿到的Info:{propsInfo}
      </div>
    )
  }
}
const NewDemo2 = connectStore(Demo2)
// 在父组件中使用
class Parent1 extends React.Component {
  render() {
    return (
      <div>
        <h5>parent1:</h5>
        {this.props.children}
      </div>
    )
  }
}

class Parent2 extends React.Component {
  render() {
    return (
      <div>
        <h5>parent2:</h5>
        <NewDemo2 propsInfo="嵌套的propsInfo"></NewDemo2>
      </div>
    )
  }
}
export default class Parent extends React.Component {
  render() {
    return (
      <Provider value={{ contextInfo: 'contextInfo' }}>
        <NewDemo propsInfo="等层的propsInfo"></NewDemo>
        <Parent1>
          <Parent2></Parent2>
        </Parent1>
      </Provider>
    )
  }
}

多个context

import { render } from 'less'
import React from 'react'
const contextRed = React.createContext('red')
const contextBlue = React.createContext('blue')

class Child extends React.Component {
  render() {
    return (
      <contextRed.Consumer>
        {red => (
          <contextBlue.Consumer>
            {blue => (
              <div>
                <h5>多个context</h5>
                <div>第一个:{red}</div>
                <div>第二个:{blue}</div>
              </div>
            )}
          </contextBlue.Consumer>
        )}
      </contextRed.Consumer>
    )
  }
}

export default class Conent extends React.Component {
  render() {
    return (
      <contextRed.Provider value="red">
        <contextBlue.Provider value="blue">
          <Child />
        </contextBlue.Provider>
      </contextRed.Provider>
    )
  }
}

contextType

简化Consumer的操作,直接引入context 并设置就可以使用

  • 在使用方 设置static contextType 可在组件全局使用context
import React from 'react'
const context = React.createContext()

class Child extends React.Component {
  static contextType = context
  render() {
    const { name } = this.context
    return (
      <div>
        <h5>contextType</h5>
        <div>获取的值: {name}</div>
      </div>
    )
  }
}

class Middle extends React.Component {
  render() {
    return <Child></Child>
  }
}

export default class Content extends React.Component {
  render() {
    return (
      <context.Provider value={{ name: 'contextType' }}>
        <Middle></Middle>
      </context.Provider>
    )
  }
}

React.PureComponent

  • 会默认添加shouldComponentUpdate生命钩子,对pros和state进行浅比较,如果没有改变则返回false,阻止组件的render
import React from 'react'
export default class Demo extends React.PureComponent {
  componentDidUpdate() {
    console.log('PureComponent--demo------componentDidUpdate')
  }
  render() {
    return <div>PureComponent--demo</div>
  }
}

React.memo()

  • update更新的时候,不会渲染多次
import React from 'react'

function Pure() {
  console.log('Pure----render')
  return <div>纯函数组件</div>
}
export default React.memo(Pure)

React.createRef()

  • 定义一个属性myRef 设置的值为React.createRef()创建的ref
  • 通过在render 中的dom元素上 ref属性中设置this.myRef来设置值
import React from 'react'

export default class CreateRef extends React.Component {
  constructor(props) {
    super(props)
  }
  myRef = React.createRef()
  componentDidMount() {
    console.log('React.createRef------->', this.myRef)
  }
  render() {
    return (
      <div>
        <h2>React.createRef()</h2>
        <div ref={this.myRef}>这个demo</div>
      </div>
    )
  }
}

React.forwardRef()

  • 父组件获取到子组件中的dom
import React from 'react'

class MChild extends React.Component {
  render() {
    return <input ref={this.props.childRef} type="text" />
  }
}
const Child = React.forwardRef((props, ref) => <MChild {...props} childRef={ref}></MChild>)
export default class Parent extends React.Component {
  childRef = React.createRef()
  handle() {
    this.childRef.current.focus()
  }
  render() {
    return (
      <div>
        <h2>React.forwardRef</h2>
        <div>
          <Child ref={this.childRef}></Child>
          <button onClick={this.handle.bind(this)}>聚焦child</button>
        </div>
      </div>
    )
  }
}

hooks 的使用

16.8更新后支持

纯函数组件

  • 没有state状态
function Pure() {
  return <div>纯函数组件</div>
}
export default Pure

useState

  • useState接受默认参数,返回一个数组,数组第一个值为定义的变量名,第二个值为修改变量的方法
  • 修改state的值时,需要哦通过返回的set方法来设置
  • 需要在函数体顶部定义
import React, { useState } from 'react'

export default function UseState() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <h5>useState</h5>
      <div>
        count:{count} <button onClick={() => setCount(count + 1)}>增加</button>
      </div>
    </div>
  )
}

useEffect

副作用hook, 包含了 componentDidMount componentDidUpdate componentWillUnmount

  • 可以返回一个函数,用于消除副作用,类似componentWillUnmount时机调用
  • 可以调用多个useEffect
  • 可以传递一个值控制是否执行
  • 如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])
import { useState, useEffect } from 'react'

export default function UseEffect() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('useEffect')
  useEffect(() => {
    document.title = 'useEffect' + count
    console.log('useEffect----->')
  })
  //   调用多个
  useEffect(() => {
    console.log('useEffect----->1')
  })
  useEffect(() => {
    console.log('useEffect----->2')
  })
  //   返回函数用于清除副作用
  useEffect(() => {
    console.log('useEffect-----> return ')
    return () => {
      console.log('unmount')
    }
  })

  useEffect(() => {
    console.log('useEffect-----> name 更新了 ', count)
  }, [name]) // 仅在 count 更改时更新

  useEffect(() => {
    console.log('useEffect----->[]')
  }, [])
  return (
    <div>
      <h5>useEffect</h5>
      <div>
        count:{count} <button onClick={() => setCount(count + 1)}>增加</button>
      </div>
      <div>
        name :{name}修改:<button onClick={() => setName('updated')}>修改</button>
      </div>
    </div>
  )
}


useContext

  • 对于函数式组件,可以使用useContext 来使用context,使用简单方便
import React from 'react'

const ThemeContext = React.createContext()
const obj = { name: 'useContext' }

function Demo() {
  return (
    <div>
      <Demo2 />
    </div>
  )
}
function Demo2() {
  const context = React.useContext(ThemeContext)
  return (
    <div>
      <h2>useContext</h2>
      <div>contextinfo: {context.name}</div>
    </div>
  )
}
export default function UseContext() {
  return (
    <ThemeContext.Provider value={obj}>
      <Demo />
    </ThemeContext.Provider>
  )
}

useRef

  • 用于在函数式组件中获取dom元素
import React from 'react'
export default function UseRef() {
  const ref = React.useRef()
  const onHandle = () => {
    ref.current.focus()
  }
  return (
    <div>
      <h2>useRef</h2>
      <input ref={ref} type="text" />
      <button onClick={onHandle}>点击通过ref聚焦</button>
    </div>
  )
}

useReducer

  • 和redux类似
  • 加强版的useState, 可以自定义复杂的数据操作
import React, { useReducer } from 'react'
function init(initialCount) {
  return { count: initialCount }
}
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    case 'reset':
      return init(action.payload)
    default:
      throw new Error()
  }
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, 1, init)
  return (
    <div>
      <h2>useReducer</h2>
      count: {state.count}
      <button onClick={() => dispatch({ type: 'reset', payload: 1 })}>reset</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  )
}

useMemo

  • 可以使用useMemo设置缓存数据,有点类似vue里面的computed
  • useMemo函数的第一个函数参数,可以用于复杂的计算
import React, { useState, useMemo } from 'react'

function Child(props) {
  const userInfo = useMemo(() => {
    console.log('child----useMemo')
    return {
      name: props.name
    }
  }, [props.name])

  return <div>child: {userInfo.name}</div>
}

export default function Parent() {
  const [name, setName] = useState('')
  const [count, setCount] = useState(0)
  return (
    <div>
      <h2>useMemo</h2>
      <div>
        <Child name={name}></Child>
        <button onClick={() => setName('parent--useMemo')}>设置</button>
      </div>
      <div>
        count:{count}
        <button onClick={() => setCount(count + 1)}>设置</button>
      </div>
    </div>
  )
}

useCallback

  • 和useMemo有点类似,只有依赖改变,才会执行的第一个参数函数,而useMemo是使用的第一个参数返回的结果
  • useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
  • 一般用于事件的绑定
import React, { useCallback, useState } from 'react'

export default function UseCallback() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('hah')
  const add = () => {
    setCount(count + 1)
  }
  
  const changeName = useCallback(() => {
    // 只有count变化后,该函数才执行,否则不执行
    setName(name + 'changed')
  }, [count])
  return (
    <div>
      <h2>useCallback</h2>
      <div>
        count:{count}
        name:{name}
        <button onClick={add}></button>
        <button onClick={changeName}>设置name</button>
      </div>
    </div>
  )
}

useImperativeHandle

  • 暴露ref,可以供父组件使用
import React, { useImperativeHandle, useRef } from 'react'

const Child = React.forwardRef((props, ref) => {
  const inputRef = useRef()
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    }
  }))
  return <input ref={inputRef} type="text" />
})

export default function Parent() {
  const childRef = useRef()
  return (
    <div>
      <h2>useImperativeHandle</h2>
      <Child ref={childRef}></Child>
      <button onClick={() => childRef.current.focus()}>聚焦</button>
    </div>
  )
}

useLayoutEffect

  • 在页面渲染之前更新
import { useLayoutEffect, useEffect, useState } from 'react'

export default function UseLayoutEffect() {
  const [count, setCount] = useState(0)
  // 使用useEffect 会产生二次渲染
  //   useEffect(() => {
  //     if (count === 0) {
  //       setCount(10 + Math.random() * 200)
  //     }
  //   }, [count])
  // 不会产生二次渲染
  useLayoutEffect(() => {
    if (count === 0) {
      setCount(10 + Math.random() * 200)
    }
  }, [count])
  return (
    <div>
      <h2>useLayoutEffect</h2>
      <div>uselayouteffect: {count}</div>
      <button onClick={() => setCount(0)}>+</button>
    </div>
  )
}

useDebugValue

  • 调试工具使用的
  useDebugValue(count ? 'Online' : 'Offline')

自定义hooks

import { useState, useEffect } from 'react'

function useMyHooks(props) {
  const [value, setValue] = useState(props)
  useEffect(() => {
    console.log('useEffect----->useMyHooks')
    setValue(6)
  })
  return value
}

export default function MyHooks() {
  const value = useMyHooks(9)
  const [count, setCount] = useState(0)
  return (
    <div>
      <h5>自定义hooks</h5>
      <div>
        count:{count}获取的值: {value}
        <button onClick={() => setCount(count + 1)}>增加</button>
      </div>
    </div>
  )
}

使用规则

  • 只在函数顶部使用
  • 只在React函数组件中使用
  • 不要在条件语句中调用hook

Portals

Portals 提供了一种一流的方式来将子组件渲染到存在于父组件的 DOM 层次结构之外的 DOM 节点中。结构不受外界的控制的情况下就可以使用portals进行创建

ReactDom.createPortal()

  • 可以在根节点外挂载到指定节点
  • 常见的有model模态框
import React from 'react'
import ReactDom from 'react-dom'
const model = document.getElementById('model')
class CreatePortal extends React.Component {
  constructor(props) {
    super(props)
    this.el = document.createElement('div')
  }
  componentDidMount() {
    model.appendChild(this.el)
  }
  componentWillUnmount() {
    model.removeChild(this.el)
  }
  render() {
    return ReactDom.createPortal(this.props.children, this.el)
  }
}

export default class Demo extends React.Component {
  render() {
    return (
      <div>
        <h2>React.createPortal()</h2>
        <CreatePortal>
          <div>内容</div>
        </CreatePortal>
      </div>
    )
  }
}

事件冒泡

在 #app-root 里的 Parent 组件能够捕获到未被捕获的从兄弟节点 #modal-root 冒泡上来的事件。

import React from 'react'
import ReactDom from 'react-dom'
const model = document.getElementById('model')
class CreatePortal extends React.Component {
  constructor(props) {
    super(props)
    this.el = document.createElement('div')
  }
  componentDidMount() {
    model.appendChild(this.el)
  }
  componentWillUnmount() {
    model.removeChild(this.el)
  }
  render() {
    return ReactDom.createPortal(this.props.children, this.el)
  }
}
function Child() {
  // 这个按钮的点击事件会冒泡到父元素
  // 因为这里没有定义 'onClick' 属性,定义了只要不拦截,也会冒泡到父组件
  return (
    <div className="modal">
      <button onClick={() => console.log('portals')}>Click</button>
    </div>
  )
}
export default class Demo extends React.Component {
  state = { clicks: 0 }
  handleClick() {
    // 当子元素里的按钮被点击时,
    // 这个将会被触发更新父元素的 state,
    // 即使这个按钮在 DOM 中不是直接关联的后代
    this.setState(state => ({
      clicks: state.clicks + 1
    }))
  }
  render() {
    return (
      <div onClick={this.handleClick.bind(this)}>
        <h2>React.createPortal()</h2>
        <p>Number of clicks: {this.state.clicks}</p>
        <CreatePortal>
          <Child></Child>
        </CreatePortal>
      </div>
    )
  }
}

Profiler

用于计算渲染时长,用于辅助定位页面性能问题

评论0

登录后参与评论。

还没有评论,来抢沙发吧。

回到顶部