react 常用api及作用

483 阅读5分钟

useEffect

自己总结

useReducer

多个state需要一起更新时,就应该考虑使用useReducer,使用useReducer能够提高应用的性能

使用useReducer可以减少useState的使用,减少VDOM的render次数,也便于统一管理多变量

比如登录页面,多个状态,登陆成功要设置好多state,登录失败也要设置好多 登录页面例子

image.png 您也可以延迟创建初始状态。为此,您可以将init函数作为第三个参数传递。初始状态将设置为init(initialArg)。第三个参数是函数,延时处理 image.png

useMomo和usecallback

使用usecallback

当父组件传递状态给子组件的时候,memo好像没什么效果,子组件还是执行了,这时候我们就要引入hooks的useCallback、useMemo这两个钩子了。

//子组件会有不必要渲染的例子
interface ChildProps {
    name: string;
    onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
    console.log('子组件?')
    return(
        <>
            <div>我是一个子组件,父级传过来的数据:{name}</div>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
const ChildMemo = memo(Child);

const Page = (props) => {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('Child组件');

    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo name={name} onClick={(newName: string) => setName(newName)}/>
        </>
    )
}


在上面代码基础上,父级调用子级时,在onClick参数上加上useCallback,参数为[],则第一次初始化结束后,不在改变。

  <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/>
            {/* useCallback((newName: string) => setName(newName),[]) */}
            {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数
        </>

使用usememo

interface ChildProps {
    name: { name: string; color: string };
    onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
    console.log('子组件?')
    return(
        <>
            <div style={{ color: name.color }}>我是一个子组件,父级传过来的数据:{name.name}</div>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
const ChildMemo = memo(Child);

const Page = (props) => {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('Child组件');
    
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <ChildMemo 
                name={{ name, color: name.indexOf('name') !== -1 ? 'red' : 'green'}} 
                onClick={ useCallback((newName: string) => setName(newName), []) }
            />
        </>
    )
}

更新属性name为对象类型,这时子组件还是一样的执行了,在父组件更新其它状态的情况下,子组件的name对象属性会一直发生重新渲染改变,从而导致一直执行,这也是不必要的性能浪费。 解决这个问题,使用name参数使用useMemo,依赖于State.name数据的变化进行更新

 <ChildMemo 
                //使用useMemo,返回一个和原本一样的对象,第二个参数是依赖性,当name发生改变的时候,才产生一个新的对象
                name={
                    useMemo(()=>({ 
                        name, 
                        color: name.indexOf('name') !== -1 ? 'red' : 'green'
                    }), [name])
                } 
                onClick={ useCallback((newName: string) => setName(newName), []) }
                {/* useCallback((newName: string) => setName(newName),[]) */}
                {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数
            />

总结

在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可。而在使用函数的情况,需要考虑有没有函数传递给子组件使用useCallback。而在值有所依赖的项,并且是对象和数组等值的时候而使用useMemo(当返回的是原始数据类型如字符串、数字、布尔值,就不要使用useMemo了)。不要盲目使用这些hooks

react.createcontext

image.png

image.png

image.png 引用不能使用函数组件,只能用类组件

必须先用组件引用下,要不获取到的是空对象 image.png 或者这种洗写法也行

image.png

函数组件都可以消费

image.png

最好的使用useContext来消费

import React, { useContext } from "react";
import ReactDOM from "react-dom";

const TestContext= React.createContext({});

const Navbar = () => {
  const { username } = useContext(TestContext)

  return (
    <div className="navbar">
      <p>{username}</p>
    </div>
  )
}

const Messages = () => {
  const { username } = useContext(TestContext)

  return (
    <div className="messages">
      <p>1 message for {username}</p>
    </div>
  )
}

function App() {
  return (
	<TestContext.Provider 
		value={{
			username: 'superawesome',
		}}
	>
		<div className="test">
			<Navbar />
			<Messages />
		</div>
	<TestContext.Provider/>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

useImperativeHandle

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值,说简单点就是,子组件可以选择性的暴露给副组件一些方法,这样可以隐藏一些私有方法和属性,官方建议,useImperativeHandle应当与 forwardRef 一起使用,具体如何使用看下面例子

  const kun = useRef()

  const introduce = useCallback (() => {
    console.log('i can sing, jump, rap, play basketball')
  }, [])
  useImperativeHandle(ref, () => ({
    introduce: () => {
      introduce()
    }
  }));

  return (
    <div ref={kun}> { props.count }</div>
  )
}

const KunKun = forwardRef(Kun)

function App () {
  const [ count, setCount ] = useState(0)
  const kunRef = useRef(null)

  const onClick = useCallback (() => {
    setCount(count => count + 1)
    kunRef.current.introduce()
  }, [])
  return (
    <div>
      点击次数: { count }
      <KunKun ref={kunRef}  count={count}></KunKun>
      <button onClick={onClick}>点我</button>
    </div>
    )
}

ref

创建ref3种方式

声明

image.png

引用 image.png

调用

image.png

image.png

useRef

不仅可以引用还可以保存数据

image.png

createref和useref区别

受控组件

image.png

react.lazy和suspense

基本使用 基本使用2

高阶组件

应用场景

增强props

1,当子组件每一个都要添加一个属性,不用修改外部组件,只需要修改子组件内部即可

image.png

渲染判断鉴权

image.png

生命周期劫持

反向继承


class ComponentChild extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      num: 2019
    }
  }
  componentDidMount() {
    console.log("child component Did Mount")
  }
  clickComponent() {
  // this.setState({num:2})
  }
  render() {
    return (
      <div>{this.state.num}</div>
    )
  }
}
let iihoc = WrapComponet => class extends WrapComponet {
  constructor(props) {
    super(props)
    this.state = {
      num: 2000
    }
  }
  componentDidMount() {
    console.log('iihoc componentDidMount')
    console.log(this)
    this.clickComponent()
    // this.setState((pre) => ({ num: pre.num + 1 }))
  }
  render() {
    return (
      <div>
        <div onClick={this.clickComponent}>'iiHoc 点击'</div>
        这样的方式比属性代理好的是,在外部组件可以调用被继承组件的方法。但不能将被继承的 state 和 钩子覆盖掉
        {/* <div><WrapComponet /></div> */}
        
        
        这样的方式,外部组件的 state 可以将,被继承组件的 state 和 钩子函数彻底覆盖掉。同时,外部组件也可以调用被继承组件的方法。
        <div>{super.render()}</div>
      </div>
    )
  }

}
export default iihoc(ComponentChild)

常用的高阶组件

connect,withrouter,forwardRef

直接在函数组件上加ref会报错,需要使用forwardRef包装

image.png image.png

portals

image.png

strictMode

image.png

image.png

自定义hooks

image.png

自定义hooks写的非常好的文章