react扩展,setState、lazyLoad、hooks、Fragment、Context、pureComponent、renderProps、ErrorBoundary

-

一、setState的两种用法

setState是异步的调用,当我们代用过setState后,紧接着打印state的值,发现是更改之前的值。

对象式和函数式

复制代码
import React, { Component } from 'react'

export default class Demo extends Component {
  state = { count: 0 };
  add = () => {
    // 对象式的setState
    // // setState是异步的
    // // 第二个可选参数是个回调函数,是在值更新后,render调用后再调用
    // this.setState({count: this.state.count+1}, () => {
    //   console.log(this.state.count, '值更改之后'); // 1
    // });
    // console.log(this.state.count, 'this.state.count=='); // 0

    // 函数式的setState
    // 函数的参数可以接受到state和props
    this.setState((state, props) => {
      return { count: state.count+1}
    })

    // 对象式的setState是函数式的简写方式(语法糖)
    // 如果依赖于原来的状态,推荐用函数式的
  }
  render() {
    return (
      <div>
        <h2>当前求和为:{this.state.count}</h2>
        <button onClick={this.add}>点我加1</button>
      </div>
    )
  }
}
复制代码

二、路由的lazyLoad

懒加载

若果不用懒加载的形式,当项目加载时,会把所有的组件一下加载下来,会导致首次加载的资源很多,首页出现白屏的时间很长

懒加载是调用react中的lazy方法实现路由来加载的,还会用到react中自带的一个组件Suspense,这个组件用来指定组件未加载的时候显示的内容,可以是node节点,可以是组件,不过这个组件不能是懒加载。

复制代码
import React, { lazy, Suspense } from 'react'
import { NavLink, Route, Routes } from 'react-router-dom'
// import Home from '../Home'
// import About from '../About'
// 懒加载
const Home = lazy(() => import('../Home'))
const About = lazy(() => import('../About'))

export default class Demo extends React.Component {
  render() {
    return (
      <div className="container">
        <div className="main">
          <aside className="aside">
            { /**
             * 编写路由链接
             * 在react中,靠路由连接切换组件
             */}
              <NavLink className="btn" to="/home">home</NavLink>
              <NavLink className="btn" to="/about">about</NavLink>
          </aside>
          <div className="content">
            {/**
             * 注册路由
             */}
             {/* Suspense组件是指定在路由对应的组件未加载之前显示的内容,可以是组件 */}
             <Suspense fallback={<h1>路由为加载之前</h1>}>
                <Routes>
                    <Route path="/home" element={<Home/>}></Route>
                    <Route path="/about" element={<About/>}></Route>
                </Routes>
             </Suspense>
              
              
          </div>
        </div>
      </div>
    )
  }
}
复制代码

 三、hooks

1、hooks是react16.8版本增加的新特性

2、它可以让你在函数组件中使用state以及其他特性

三个常用的hook

useState、useEffect、useRef

复制代码
import React from 'react'
// 类式组件
// class Demo extends React.Component {
//   state = { count: 0 }

//   myRef = React.createRef()

//   add = () => {
//     this.setState({count: this.state.count+1})
//   }

//   componentDidMount() {
//     setInterval(() => {
//       this.setState(state => ({count: state.count+1}))
//     }, 1000)
//   }

//   unmount = () => {
//     const { root } = this.props;
//     root.unmount(); // 18版本
//     // ReactDOM.unmountComponentAtNode(document.getElementById('root'))
//   }

//   componentWillUnmount(){
//     console.log('组件即将卸载');
//   }

//   show = () => {
//     alert(this.myRef.current.value)
//   }
//   render() {
//     return (
//       <div>
//         <input ref={this.myRef} type="text" />
//         <h1>当前求和为:{this.state.count}</h1>
//         <button onClick={this.add}>加1</button>
//         <button onClick={this.unmount}>卸载组件</button>
//         <button onClick={this.show}>点击提示数据</button>
//       </div>
//     )
//   }
// }

// 函数式组件
function Demo(props) {
  // 每次状态改变都会调用一次函数组件(相当于render)
  console.log('demo');
  // useState 接收一个参数,是状态的初始值
  // 返回值是一个数组,数组内只有两个值,第一个是状态,第二个是更新状态的方法
  // 每次改变状态都会调用demo函数,但为什么useState的状态不初始化呢?是因为react底层做处理了,当第一次调用useState后,就会把值存起来
  const [count, setCount] = React.useState(0);

  function add() {
    // setCount(count+1); // 第一种写法
    setCount(count => count+1); // 第二种传一个函数
    console.log(count, 11111); // setState是异步的
  }
  // useEffect hooks 可以模拟出componentDidMount、componentDidUpdate、componentWillUnmount三个生命周期
  // 有两个参数,第一个参数是一个函数,第二个函数是指定要监听的数据,是一个数组
  // 当没有指定第二个参数时,相当于检测所有人,每次有状态更新就会执行第一个参数(方法),相当于componentDidUpdate
  // 如果第二个参数传了,一个空数组,就代表谁都不监听,只有在组件挂载的时候调用一次,相当于componentDidMount钩子
  // 第一个参数(是个函数)返回的函数,相当于componentWillUnmount
  React.useEffect(() => {
    console.log('监听所有状态,相当于componentDidUpdate');
  });
  React.useEffect(() => {
    console.log('只有页面挂载时调用,相当于componentDidMount生命周期钩子');
    let timer = setInterval(() => {
      setCount(count => count+1);
    }, 1000);
    return () => {
      console.log('组件卸载前会执行,相当于componentWillUnmount');
      clearInterval(timer)
    }
  }, [])

  function unmount() {
    const { root } = props;
    root.unmount(); // 18版本
  }
  // useRef  可以在函数组件中存储/查找组件内的标签或任意其它数据
  // 功能和React.createRef一样
  const myRef = React.useRef();
  function show() {
    alert(myRef.current.value)
  }
  return (
    <div>
      <input ref={myRef} type="text" />
      <h1>当前求和为:{count}</h1>
      <button onClick={add}>加1</button>
      <button onClick={unmount}>卸载组件</button>
      <button onClick={show}>点击提示数据</button>
    </div>
  )
}

export default Demo
复制代码

 四、Fragment

 Fragment 碎片,组件都需要有一个顶层元素,而且只有一个,但有时候,我们不想让组件有顶层元素,想成为父组件的直接子元素,那么Fragment就可以充当顶层元素,而且不会渲染。

复制代码
import React, { Component, Fragment } from 'react'

export default class Demo extends Component {
  render() {
    return (
      // Fragment标签在渲染时,react会把它丢去,Fragment只能接受key一个属性
      <Fragment key={1}>
        <input type="text" />
        <input type="text" />
      </Fragment>

      // 写一个空标签和Fragment的效果一样,但是空标签不能加key以及任何属性
      // <>
      //   <input type="text" />
      //   <input type="text" />
      // </>
    )
  }
}
复制代码

 五、Context

React.createContext

一种组件间通信方式,常用语【祖组件】与【后代组件】间的通信

在应用开发中,一般不用context,一般用它去封装插件

复制代码
import React, { Component } from 'react'
import './index.css'

// 创建context对象
const MyContext = React.createContext();

export default class A extends Component {
  state = { username: 'tom', age: 18 }

  changeName = () => {
    this.setState({username: 'jack'})
  }
  
  render() {
    const { username, age } = this.state;
    
    return (
      <div className="parent">
        <h3>我是A组件</h3>
        <h4>我的用户名是:{username}</h4>
        <MyContext.Provider value={{ username, age }}>
          <B/>
        </MyContext.Provider>
        <button onClick={this.changeName}>改名</button>
      </div>
    )
  }
}

class B extends Component {
  render() {
    return (
      <div className="child">
        <h3>我是B组件</h3>
        <h4>我从A组件接收到的用户名是:{'xxx'}</h4>
        <C/>
      </div>
    )
  }
}

// class C extends Component {
//   // 声明接收context,之后才能从this.context中拿到值
//   static contextType = MyContext; // 只能是类式组件使用
//   render() {
//     return (
//       <div className="grand">
//         <h3>我是C组件</h3>
//         <h4>我从A组件接收到的用户名是:{this.context.username}, 年龄是:{this.context.age}</h4>
//       </div>
//     )
//   }
// }

function C() {
  return (
    <div className="grand">
      <h3>我是C组件</h3>
      <h4>我从A组件接收到的用户名是:
        {/* Consumer 函数式组件和类式组件都可以用 */}
        <MyContext.Consumer>
          { value => `${value.username}, 年龄是:${value.age}` }
        </MyContext.Consumer>
      </h4>
      
    </div>
  )
}
复制代码

 六、pureComponent

组件优化:

1、只要调用setState(),即使不改变状态数据,组件也会调用render

2、只要当前组件调用render、就会调用子组件的render,效率低(即使子组件没用父组件的值)

原因是component中的shouldComponentUpdate总是返回true

怎么解决呢?

1、手动对比props、state的值,如果变了让组件更新,如果没变,就不让更新,但是属性多了,就很麻烦

2、React.pureComponent会自动帮我们解决

复制代码
import React, { Component, PureComponent } from 'react'
import './index.css'
// PureComponent 可以解决以上问题
export default class Prent extends PureComponent {

  state = { carName: '奔驰c63'}

  changeCar = () => {
    this.setState({carName: '迈巴赫'})
  }
  // 手动解决组件render问题
  // shouldComponentUpdate(nextProps, nextState) {
  //   console.log(nextProps, nextState); // 将要变成的数据
  //   console.log(this.props, this.state); // 当前的数据
  //   return !(nextState.carName === this.state.carName);
  // }
  render() {
    console.log('parent--render');
    const { carName } = this.state;
    return (
      <div className="parent">
        <h3>我是prarent组件</h3>
        <span>我的车名字是: {carName}</span><br />
        <button onClick={this.changeCar}>点我换车</button>
        <Child carName={'奥拓'} />
      </div>
    )
  }
}

class Child extends PureComponent {
  render() {
    console.log('child--render');
    return (
      <div className="child">
        <h3>我是child组件</h3>
        <sapn>我接到的车是:{this.props.carName}</sapn>
      </div>
    )
  }
}
复制代码

 

 七、renderProps  (类似vue中的slot)

react中类似slot的技术有两种,一种是childrenProps、另一种是renderProps

renderProps可以拿到父组件内部的状态传给子组件

组件嵌套有两种形式

第一种形式childrenProps

<A>
  <B/>
<A/>
// A组件内
{this.props.children}

如果用第一种形式,有一个问题,如果A内有状态需要传给B,就没有办法了。

这里就请出了renderProps,renderProps其实就是给组件传一个名为render的props,值为一个函数,该函数在组件内部执行,可以把组件内部的状态带过来

复制代码
import React, { Component } from 'react'
import './index.css'

export default class parent extends Component {
  render() {
    return (
      <div className="parent">
        <h3>我是parent组件</h3>
        {/* <A>
          <B />
        </A> */}
        {/* 渲染单个子组件 */}
        {/* <A render={(name) => <B name={name} />} /> */}
        {/* 渲染多个子组件 */}
        <A render={
          (name) =>
            <React.Fragment>
              <B name={name} />
              <C name={name} />
            </React.Fragment>
          } />
      </div>
    )
  }
}

class A extends Component {
  state = {name: 'tom'}
  render() {
    return (
      <div className="a">
        <h3>我是A组件</h3>
        {/* {this.props.children} */}
        {this.props.render(this.state.name)}
      </div>
    )
  }
}

class B extends Component {
  render() {
    return (
      <div className="b">
        <h3>我是B组件, 我接收到的name是:{this.props.name}</h3>
      </div>
    )
  }
}

class C extends Component {
  render() {
    return (
      <div className="c">
        <h3>我是C组件, 我接收到的name是:{this.props.name}</h3>
      </div>
    )
  }
}
复制代码

 这里推荐一个启动静态服务器的插件:serve

之前有用过http-server,这里又有一个

npm i serve -g

项目打包后,执行 serve build(文件路径)  就可以启动起来一个服务器

 八、错误边界  ErrorBoundary

程序内如果某个组件出现错误,会导致整个系统不能运行,错误边界就是把错误限制在某个组件,不让影响整个系统

处理错误边界都是在父组件内处理

用到两个钩子函数:

getDerivedStateFromError   // 子组件报错时会调用这个钩子,而且把错误信息传来了
componentDidCatch  // 渲染组件时出错会调用这个钩子
父组件:
复制代码
import React, { Component } from 'react'
import Child from './children'
import './index.css'

export default class Parent extends Component {

  state={
    hasError: '', // 用于标识子组件是否产生错误
  }

  // static getDerivedStateFromProps (当组件的状态完全由props传来时)
  // 子组件报错时会调用这个钩子,而且把错误信息传来了
  // getDerivedState 英文意思是获取一个衍生的状态
  // 错误边界始终找出错组件的父组件去处理的
  static getDerivedStateFromError(error) {
    return { hasError: error }
  }
  // 渲染组件时出错
  componentDidCatch() {
    console.log('渲染组件时出错');
    // 一般在这个钩子里统计错误次数,反馈给服务器,通知程序人员进行bug解决
  }
  render() {
    return (
      <div className="parent">
        <h3>我是parent组件</h3>
        {
          this.state.hasError
          ? <h4>当前子组件报错,稍后再试</h4>
          : <Child />
        }
        
      </div>
    )
  }
}
复制代码

 

 

 

 

 

 

 

 

-

posted @   古墩古墩  Views(95)  Comments(0Edit  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
历史上的今天:
2019-10-22 pc页面自动缩放到手机端
点击右上角即可分享
微信分享提示