Bootstrap

React学习06- API扩展

setState

更新状态的两种写法

setState(stateChange,[callback]) // 传入对象参数,callback为可选的回调函数,在状态、页面都更新,render调用之后,才被调用
setState(updater,[callback]) // 传入方法参数,updater是返回一个状态对象的函数,可以接收到state和props

setState是异步更新,setState是同步的,它之后做的更新动作是异步的

// 先打印旧count,然后再+1
this.setState({count:count+1})
console.log(this.state.count)

// 等待更新之后打印+1
this.setState({count:count+1},()=>{
  console.log(this.state.count)
})
// 函数式setState
this.setState((state,props)=>{
  return {count:state.count+1}
})

如果新状态不依赖于原状态,使用对象式;
如果新状态依赖原状态,使用函数式;
如果需要在setState执行之后获取最新的状态数据,需要在第二个参数callback函数中获取。

lazyLoad

路由组件的懒加载,当页面使用路由导航,第一次加载页面时会把所有路由组件都请求一次,懒加载可以做到需要哪个再请求哪个。

import {lazy,Suspense} from 'react'
import Loading from './Loading'
// 不使用import,使用定义变量的形式引入路由组件
const  Demo = lazy(()=>import('./Demo'))
// 使用Suspense包裹路由组件,当点击导航还未返回结果时显示fallback里的虚拟DOM
<Suspense fallback={<Loading/>}>
  <Route path="/demo" component={Demo}/>
</Suspense>

Hooks

Hook是React16.8.0新增的语法特性,函数式组件没有自己的this,所以很多特性都用不了,在16.8以后的版本可以在函数组件中使用state和其他React特性。

三个常用的Hook

State Hook: React.useState()
Effect Hook: React.useEffect()
Ref Hook: React.useRef()

React.useState()

useState()的参数在第一次初始化时指定的值在内部做缓存,返回值是包括2个元素的数组,第一个是内部当前的状态,第二个是更新状态的函数。

const [状态变量,更新状态变量的方法] = React.useState(状态变量初始值)
// 更新状态变量的方法,两种写法
setXXX(newValue)
setXXX(value => newValue)
function Demo(){
  console.log('Demo') //点击+1按钮,Demo就执行一次
  const [count,setCount] = React.useState(0) // React底层做了处理,不会重复执行
  function add(){
    // setCount(count+1)
    setCount(count => count+1)
  }
 return (
   <div>
     <h1>当前和:{count}</h1>
     <button onClick={add}>点击+1</button>
   </div>
 )
}

React.useEffect()

useEffect类似监听器,可以用于模拟生命周期钩子,可以看作是componentDidMount(), componentDidUpdate(), componentWillUnmount()的组合

useEffect(()=>{
  // 在此执行副作用操作(发送ajax请求,订阅,启动定时器,手动修改真实DOM...)
  ...componentDidMount(), componentDidUpdate()
  return ()=>{
    // 在此做收尾工作 componentWillUnmount
  }
}, [监视的变量]) // 如果指定为[],回调函数只会在第一次render()后执行;当监视的变量有更新时,调用一次useEffect
React.useEffect(()=>{
  let timer = setInterval(()=>{
    setCount(count => count+1)
  },1000)
  return ()=>{
    clearInterval(timer)
  }
},[count])

React.useRef()

useRef可以在函数式组件中存储/查找组件内标签或其他数据,用于保存标签对象,功能和React.createRef()一样

function Demo(){
  const myRef = React.useRef()
  function show(){
    alert(myRef.current.value)
  }
  return (
    <div>
      <input type="text" ref={myRef}>
      <button onClick={show}>展示输入值</button>
    </div>
  )
}

Fragment

Fragment用于代替组件中的根标签

<Fragment></Fragment>
<></> // 也可以使用空标签代替
render(){
  return (
    <Fragment>
      <input type="text"/>
    </Fragment>
  )
}

Context

context用于组件与子孙组件之间的通信,开发中一般不用context,一般在开发react插件时使用

// 1.先创建Context对象,要放在要通信的组件都能访问到的地方
const MyContext = React.createContext()
class A extends Component{
  state = {name:'tom'}
  render(){
    return (
      <>
        <h1>A: {this.state.name}</h1>
        <MyContext.Provider value={name}>
          <B/>
        </MyContext.Provider> // 2. 被MyContext包裹的组件及其子组件都能使用传递的value
      </>
    )
  }
}
class B extends Component{
  render(){
    return (
      <>
        <h1>B: </h1>
        <C/>
      </>
    )
  }
}
class C extends Component{
  static contextType = MyContext // 3.方法1. 必须声明才能使用
  render(){
    return (
      <>
        <h1>C: {this.context}</h1> // 4.方法1.使用
      </>
    )
  }
}

function C(){
  return (
      <>
        <h1>C: 
          <MyContext.Consumer>
            {
              value => {
                return `${value.name}`
              }
            }
          </MyCOntext.Consumer>
        </h1> // 4.方法2.使用
      </>
    )
}

组件优化

Component的两个问题:
1.只要执行setState(),即使不改变状态数据,组件也会重新render()
2.只当前组件重新render(),就会自动重新render子组件,效率低
因为组件中的shouldComponentUpdate()总是返回true。

做到只要当组件的state或props数据发生改变时才重新render(),
办法1,重写shouldComponentUpdate(),比较新旧state或props数据,有变化才返回true,没有则返回false。

shouldComponentUpdtae(newProps,newState){
  if(oldState!=newState)return true;
  return false;
}

办法2,使用PureComponent,PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true。
注意只是进行了state和props数据的浅比较,如果只是数据对象内部数据变了,返回false,不要直接修改state数据,而是要产生新数据。

import {PureComponent} from 'react'
class A extends PureComponent{...}

render props

类似Vue的插槽,在组件内预留一个位置,可以显示需要的其他组件。

<A>hello</A>  // 在页面不会显示hello,需要在A组件中使用this.props.children

export default class Parent extends Component{
  render(){
    return(  
      <div>
        <A><B/></A>  // 不在A组件内使用B组件形成父子关系
      </div>
    )
  }
}
class A extends Component{
  render(){
    return (
      <div>
        {this.props.children}// 必须在A组件中需要显示B组件的位置使用这行语句,才可以显示B组件,
      </div>
    )
  }
}
export default class Parent extends Component{
  render(){
    return(  
      <div>
        <A render={(name) => <B name={name} />}/> // 箭头函数返回一个组件
      </div>
    )
  }
}
class A extends Component{
  state = {name:'tom'}
  render(){
    return (
      <div>
        A组件内有: {this.props.render(name)} // 将Parent中,A组件里的箭头函数体内容显示在这里,这里是B组件,并传递了A组件的值给B组件去显示
      </div>
    )
  }
}
class B extends Component{
  render(){
    return (
      <div>
        A组件的数据:{this.props.name}
      </div>
    )
  }
}

错误边界

用于捕获后代组件错误,渲染出备用页面。只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。

// 生命周期函数,一旦后台组件报错就会触发,在容易报错的组件的父组件使用
static getDrivedStateFromError(error){
  console.log(error) // 错误信息
  // 在render之前触发,返回新的state
  return {hasError: true}
}

componentDidCatch(error,info){
  // 统计页面的错误,发送请求到后台
  console.log(error,info)
}

组件通信方式总结

  1. props(父子组件): (1) children props (2) render props
  2. 消息订阅发布(兄弟组件、跨级组件): pubs-sub
  3. 集中式管理(兄弟组件、跨级组件): redux
  4. conText(跨级组件):生产者-消费者模式
;