React

36 阅读7分钟

react

项目搭建

npx create-react-app  项目名称

npx 会自动去查找当前依赖包中胡可执行文件,如果找不到就会去环境变量中查找,找不到就会帮你安装。

如果想要 安装全局create-react-app :

npm i -g create-react-app  //全局安装

create-react-app 项目名称  //创建 

create-react-app创建的项目webpack等配置信息都是封装好的,为了灵活修改相关配置可以运行

npm run eject //暴露配制项

生命周期

只有class组件才会有生命周期,组件实例从被创建到被销毁的过程称为组件的生命周期。

挂载
  • constructor(): 通过给this.state 赋值初始化的地方、为事件处理函数绑定事件。 在 React 组件挂载之前,会调用它的构造函数。
  • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
  • render(): 是 class 组件中唯一必须实现的方法。
  • componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。
更新

每当组件的 state 或 props 发生变化时,组件就会更新。

  • getDerivedStateFromProps(): 在调用 render 方法之前调用。

  • shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。

  • render()

  • getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。能在组件发生更改之前从DOM中捕获一些信息,比如滚动位置,此生命周期方法的任何返回值将作为参数传递给componentDidUpdate()

  • componentDidUpdate(): 在更新后会被立即调用。

    卸载
  • componentWillUnmount(): 在组件卸载及销毁之前直接调用。

组件传值

父组件 ->子组件

子组件通过props接受

import React, { Component } from 'react'
import Son from '../Son'
//父组件
export default class Father extends Component {
  constructor() {
    super();
    this.state = {
      age: 666,
      title: '昨天,今天,明天'
    }
  }
  render() {
    return (
      <div>
     //两种写法
        <Son {...this.state}  value={this.state.age} />
      </div>
    )
  }
}

import React, { Component } from 'react'
//子组件
export default class Son extends Component {
  constructor(props) {
      super();
    this.state = { name: this.props.title}
  }
  render() {
    return (
      <div>
        <h1>{this.state.name}</h1>
        <h1>{this.props.age}</h1>
        <h1>{this.props.value}</h1>
      </div>
    )
  }
}

子组件->父组件

子组件调用父组件的方法,子组件调用,将要传递的数据,作为回调函数的参数。

import React, { Component } from 'react'
import Son from '../Son'
//父组件
export default class Father extends Component {
  constructor() {
    super();
    this.state = {
      age: 666,
      title: '昨天,今天,明天'
    }
  }
   changeAge(data){
       this.setState({age:666})
   }
  render() {
    return (
      <div>
        <h1>{this.state.age}</h1>
        <Son {this.state.age} changeAge={this.changeAge.bind(this)}/>
      </div>
    )
  }
}
import React, { Component } from 'react'
//子组件
export default class Son extends Component {
  constructor(props) {
      super();
    this.state = { name: this.props}
  }
 //  使用了箭头函数,使用时不需要重新绑定this
  handleChange = (e) => {   
    this.props.changeAge(e.target.value)
  }
  render() {
    return (
      <div>
            
         <input type="text" value={this.props.value} onChange={this.handleChange} />
      </div>
    )
  }
}
兄->弟

将状态共享,提升到最近的公共父组件中,由父组件管理状态

import React, { Component } from 'react'
import Son from '../Son'
import Daughter from '../Daughter'
//父组件
export default class Father extends Component {
  constructor() {
    super();
    this.state = {
      age: 666,
    }
  }
  addAge=()=> {
    this.setState({ age: this.state.age - 1 })
  }
  render() {
    return (
      <div>
        <Daughter value={this.state.age}/>
        <Son addAge={this.addAge}/>
      </div>
    )
  }
}
import React, { Component } from 'react'
//子组件Son
 export default class Son extends Component {
  render() {
    return (
      <div>
        <button onClick={() => { this.props.addAge() }}>按钮</button>
      </div>
    )
  }
}
import React, { Component } from 'react'
//子组件Daughter
export default class Daughter extends Component {
    render() {
        return (
            <div>
                <h2>{this.props.value}</h2>
            </div>
        )
    }
}

设置全局eventbus事件总线进行通讯

npm add -D events
import { EventEmitter } from 'events'
//导入事件总线,利用这个对象发射和监听事件,这个对象是全局的
const eventBus = new EventEmitter()
export default eventBus
import React, { Component } from 'react'
import eventBus from './event'
//需要传递事件的组件
export default class Father extends Component {
    sendEvent = () => {
        let arr = [1, 23, 4]
        let obj = { name: 'Y' }
        //发送事件eventBus.emit('事件名',参数)
        eventBus.emit('eventName', arr, obj)
    }
    render() {
        return (
            <div>
                <button onClick={this.sendEvent}>按钮</button>
            </div>
        )
    }
}
import React, { Component } from 'react'
import eventBus from './event'
//接受事件的组件
export default class Brother extends Component {
    // 添加事件监听
    componentDidMount() {
        eventBus.addListener('eventName', this.event)
    }
    event(a, b) {
        console.log(a, b)
    }
    render() {
        return (
            <div>Brother</div>
        )
    }
    //移除监听事件
    componentWillUnmount() {
        eventBus.removeListener('eventName', this.event)
    }
}

Context 跨组件传递数据

无需为每层组件手动添加props,就能在组件树间进行数据传递的方法,当前的context的值由上层组件中距离当前组件最近的.Provider 决定的。

import React from 'react'
const ThemeContext = React.createContext('90')
export default ThemeContext
import React, { Component } from 'react'
import Son from '../Son'
import ThemeContext from '../ContextApi'
//父组件
export default class Father extends Component {
 render() {
    return (
      <div>
        <ThemeContext.Provider value='数据'>
          <Son />
        </ThemeContext.Provider>
      </div>
    )
  }
}
import React, { Component } from 'react'
import ThemeContext from '../ContextApi'
//孙子组件
export default class GrandSon extends Component {
    static contextType = ThemeContext;
    render() {
        return (
            <div>
                <h1>{this.context}</h1>
            </div>

        )
    }
}

推荐使用 受控组件 来处理表单数据,受控组件就是表单元素的value需要state来获取。非受控组件使用ref从Dom节点中获取表单数据。

<input type="text" value={this.state.value} onChange={(e) => { this.setState({ value: e.target.value })}} />
高阶组件

高阶组件就是一个函数,这个函数接收一个组件作为参数,并返回一个新的组件。

import React, { Component } from 'react'
function Warp(WarpComponent) {
    class hightOrder extends Component {
        state = {
            num: 88
        }
        render() {
            return (
                <WarpComponent num={this.state.num} />
            )
        }
    }
    return hightOrder

}
export default Warp


import React, { Component } from 'react'
import hightOrder from '../hightOrder'
export  class Father extends Component {
 render() {
    return (
      <div>
        <p>{this.props.num}</p>
      </div>
    )
  }
}
export default hightOrder(Father)
组件优化

父组件中的state或者props发生更新的时,会触发子组件的更新,会导致没有必要的性能浪费。

可以使用shouldComponentUpdate(nextProps,nextState)生命周期让子组件不更新 (不推荐)

可以使用PureComponent来代替 Component。PureComponent通过props和state的浅比较,代替了shouldComponentUpdate的工作,但是只能比较外层数据,只要外层相同,就认为没有变化。不适合多层嵌套对象。

可以使用memo包裹函数组件,函数组件给定相同props的情况下渲染相同的结果,包装在memo中调用,将跳过组件渲染的操作,复用最近一次渲染结果。与PureComponent相似,但只是适用于函数组件,不适用class组件。

Hooks

函数式组件没有状态生命周期,hooks可以让你不写class组件的情况下使用state以及其他的React特性。Hooks只能在函数组件中使用。

useState
//class组件
//设置变量初始值
this.state={name:'hello'}
//使用
{this.state.name}
//更新
this.setState({name:'hello world'})

//hooks
//引入useState
import React,{useState} from 'react'
//设置变量,方法
const [name,setName]=useState('hello')
//使用
{name}
//更新
setName('hello world')
useEffect

函数式组件中没有生命周期,可以使用useEffect替代,useEffect可以看做组件加载,更新,卸载的集合。

//只模拟componentDidMount
import React,{useState} from 'react'
  useEffect(()=>{
        console.log('挂载')
    },[])
// 模拟componentDidUpdate和componentDidMount
  useEffect(()=>{
        console.log('挂载更新')
    },[参数])
想检测哪个数据更新就把哪个数据写到数组里面,如果要检测所有变量的更新可以把所有变量都写到数组中、也可以删掉数组。
  useEffect(()=>{
        console.log('挂载更新')
    })
//模拟componentWillUnmount
  useEffect(()=>{
      reurn ()=>{
          console.log('卸载')
      }
    })
useRef
//class
import React,{createRef} from 'react'
inputRefs=createRef()
<input type="text" ref={this.inputRefs} />
    
//hooks
import React,{useRef} from 'react'
const inputRefs=useRef()
<input type="text" ref={inputRefs} />
  
createRef:每次渲染的时候都会创建一个ref对象
useRef:第一次渲染时创建一个对象后,之后重新渲染的时候。如果对象创建过就不在创建新的ref对象,useRef性能会更好一些。
useContext
//class --> Context 跨组件传递数据
//hooks
const themes={
background:'pink'
}
const ThemeContext = React.createContext(themes.light);
function App() {
  return (
    <ThemeContext.Provider value={themes}>
      <Toolbar />
    </ThemeContext.Provider>
  );
//取值
function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background }}>按钮</button>
  );
}

useReducer
//接收两个参数,reducer函数和参数初始值。
const [state,dispatch]=useReduer((state,action)=>{
switch(action){
case:'yes':
return '123'
default:
return '321'
}
},0)
//
<button onClick={()=>dispatch('yes')><button>
useMemo

会在渲染期间执行,仅会在某个依赖项改变时才重新计算,可以避免在每次渲染时都进行高开销计算。如果没有提供依赖项数组,useMemo在每次渲染时都会重新计算新的值。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback

useCallback(fn, deps)相当于useMemo(() => fn, deps)。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMem返回是函数的运行结果,useCallback返回为函数。

useImperativeHandle

可以让你使用ref时自定义暴露给父组件的实例。应该与forwardRef一起使用。只暴露父组件需要用到的Dom方法。

import React, { useRef, forwardRef, useImperativeHandle } from 'react'

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

export default function ImperativeHandleDemo() {
  const inputRef = useRef()
  return (
    <div>
      <button onClick={() => inputRef.current.focus()}>聚焦</button>
      <JMInput ref={inputRef} />
    </div>
  )
}
useLayoutEffect

useEffect 差不多,useLayoutEffect可以解决一些特性场景下页面闪烁问题。会阻塞渲染,尽量不要用。

function App() {
  const [count, setCount] = useState(0);
  useLayoutEffect(() => {
    if (count == 0) {
      const randomNum = 10 + Math.random()*200
      setCount(10 + Math.random()*200);
    }
  }, [count]);
  return (
      <div onClick={() => setCount(0)}>{count}</div>
  );
}

useDebugValue

允许用户推迟屏幕更新优先级不高部分。

useDebugValue(date, date => date.toDateString());