React基础用法概括

759 阅读7分钟

react组件

react组件的两种形式

函数组件

function Welcome(props){
    return <h1>Hello,{props.name}<h1/>
}

类组件

class Welcome extends React.Component {
    render(){
        return <h1>Hello,{this.props.name}<h1/>
    }
}

组件间的通讯

props

  • props的作用就是:接收传递给组件的数据。其本质是一个对象。
  • 传递数据:给组件标签添加属性 <Hello name='james' />
  • 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据 props的特点:
  1. props可以传递很多类型,包括 字符串、数字、数组、函数甚至是html标签
  2. props是只读属性,不可以修改
  3. 在类组件中,如果使用构造函数,必须将props传递给super(),否则,无法在构造函数中使用props

2.png

父传子

  • 父组件提供要传递的state数据
  • 给子组件的标签添加属性,值为state中的数据
  • 子组件中通过props接收父组件中传递的数据
//父组件
class App extends React.Component{
      state={
          lastName:'kkk'
      }
      render(){
          return(
              <div className='App'>
                  <Child name={this.state.lastName} />
              </div>
          )
      }
  }
  //子组件
  const Child = (props) => {
      console.log(props);
      return (
          <div className='Child'>
              <p>子组件:{props.name}</p>
          </div>
      )
  }

子传父

  • 父组件提供回调函数
  • 子组件调用回调函数,将要传递的数据作为回调函数的参数
  • 子组件通过props调用回调函数
  • 将子组件的数据作为参数传递给回调函数 注意this指向
//父组件
class App extends React.Component{
      state={
          parentMsg : ''
      }
      //回调函数
      getChildMsg=(data)=>{
          console.log('父组件接收到的数据:',data);
          this.setState({
              parentMsg: data
          })
      }
      render(){
          return(
              <div className='App'>
                  父组件:{this.state.parentMsg}
                  //将回调函数作为属性值传给子组件
                  <Child getMsg = {this.getChildMsg} />
              </div>
          )
      }
  }
  //子组件
  class Child extends React.Component{
      state={
          msg:'eat'
      }
      handleClick=()=>{
          //通过props调用回调函数,并将数据作为参数
          this.props.getMsg(this.state.msg)
      }
      render(){
          return(
              <div className="Child">
                  子组件:
                  <button onClick={this.handleClick}>点击给父组件传递数据</button>
              </div>
          )
      }
  }

兄弟组件

  • 将共享的状态提升到最近的公共父组件中,由公共父组件管理状态
  • 公共父组件:
  1. 提供共享的状态
  2. 提供操作状态的方法
  • 要通讯的子组件只需要通过props接收状态或操作状态的方法 3.png
  //父组件
class App extends React.Component {
    state = {
      count0
    }
    onIncrement = () => {
      this.setState({
        countthis.state.count + 1
      })
    }
    render() {
      return (
        <div className='App' >
          <Child1 count={this.state.count} />
          <Child2 onIncrement={this.onIncrement} />
        </div>
      )
    }
  }
  //子组件1
  const Child1 = (props) => {
    return (
      <h1>计数器:{props.count}</h1>
    )
  }
  //子组件2
  const Child2 = (props) => {
    return (
      <button onClick={() => props.onIncrement()}>+1</button>
    )
  }

Context

跨组件传递数据

  1. 调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件

     const { Provider, Consumer } = React.createContext()
    
  2. 使用Provider作为父节点

     <Provider>
         <div className = 'App'>
             <Child1 />
         </div>
     </Provider>
    
  3. 设置value属性,表示要传递的数据

     <Provider value='red' >
    

4.调用Consumer组件接收数据

    <Consumer>
        {
            data => <span> data参数表示接收到数据 -- {data} </span>
        }
    </Consumer>
    
const { ProviderConsumer } = React.createContext()
//父组件
class App extends React.Component {
  render() {
    return (
      <div className='App' >
        <Node />
      </div>
    )
  }
}
//子组件1
const Node = () => {
  return (
    <Provider value='pink'>
      <div className="node">
        <SubNode />
      </div>
    </Provider>
  )
}
//子组件2
const SubNode = () => {
  return (
    <div className="subnode">
      <Child />
    </div>
  )
}
//子组件3
const Child = () => {
  return (
    <div className="child">
      <Consumer>
        {
          data => <span>我是子节点:{data}</span>
        }
      </Consumer>
    </div>
  )
}

生命周期

组件从被创建到挂载到页面中运行,再到组件不用时被卸载的过程,只有类组件才有生命周期

生命周期的三个阶段

4.png

挂载阶段

5.png

6.png

更新阶段

导致更新的因素

  • setState()
  • fourceUpdata()
  • 组件接收到新的props
class App extends React.Component{
    constructor(props){
      super(props)
      this.state={
        count:0
      }
      // this.handleClick = this.handleClick.bind(this)
    }
    handleClick=()=>{
      // this.setState({
      //   count:this.state.count + 1
      // })
      this.forceUpdate()
    }
    render(){
      console.warn('生命周期钩子函数:render');
      return(
        <div className="App">
          <Counter count={this.state.count} />
          <button onClick={this.handleClick}>++</button>
        </div>
      )
    }
  }
  class Counter extends React.Component{
    render(){
      console.warn('--子组件--生命周期钩子函数:render');
      return <h1>count:{this.props.count}</h1>
    }
  }

执行顺序:

7.png

8.png

卸载阶段

组件从页面中消失

9.png

高阶组件

高阶组件是一个函数,接收要包装的组件,返回增强后的组件,以此来实现状态逻辑复用

  1. 创建一个函数,约定以with开头
  2. 指定函数的参数,参数应该以大写字母开头
  3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并将其返回
  4. 在该组件中,渲染参数组件,同时将状态通过props传递给参数组件
  5. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中

创建高阶组件

    function withMouse(WrappedComponent){
        //该组件提供复用的状态逻辑
        class Mouse extends React.Component {
            // ...提供状态和逻辑
            render(){
                // 将状态通过props传递给参数组件
                return <WrappedComponent {...this.state} />
            }
        }
        //返回组件
        return Mouse
    }

需要增强的组件

    const Position = props => {
        return (
            <p>
                鼠标当前位置:x: {props.x}, y: {props.y}
            </p>
        )
    }

调用高阶组件,并将需要增强的组件作为参数传入高阶组件中

    const MousePosition = withMouse(Position)

完整代码:

//创建高阶组件
function withMouse(WrappedComponent) {
    //该组件提供复用的状态逻辑
    class Mouse extends React.Component {
      state = {
        x0,
        y0
      }
      handleMouse = (e) => {
        this.setState({
          x: e.clientX,
          y: e.clientY
        })
      }
      componentDidMount() {
        window.addEventListener('mousemove'this.handleMouse)
      }
      componentWillUnmount() {
        window.removeEventListener('mousemove'this.handleMouse)
      }
      render() {
        //将状态通过props传递给参数组件
        return <WrappedComponent {...this.state} />
      }
    }
    //返回组件
    return Mouse
  }
  //需要增强的组件
  const Position = porps => {
    return (
      <p>
        鼠标当前位置:(x:{porps.x}, y:{porps.y})
      </p>
    ) 
  }
  //需要增强的组件
  const Image = props => {
    return(
      <img src={img} style={{
        position:'absolute',
        top:props.y,
        left:props.x
      }} />
    )
  }
  //调用高阶组件,并将需要增强的组件作为参数传入高阶组件中
  const MousePosition = withMouse(Position)
  const MouseImage = withMouse(Image)
  class App extends React.Component {
    render() {
      return (
        <div className="App">
          <h1>高阶组件</h1>
          {/* 将增强的组件渲染到页面中 */}
          <MousePosition />
          <MouseImage />
        </div>
      )
    }
  }

react路由

路由的功能,从一个视图跳转到另一个视图,在React中,就是URL路径与组件的对应关系

安装路由:

    yarn add react-router-dom

导入:

    import { BrowserRouter as Router, Link, Route } from 'react-router-dom'

两种模式:BrowserRouter和HasRouter

Router组件: 包裹整个应用, 一个React应用只需要使用一次

Link组件: 用于指定导航链接(a标签)

    //to属性:浏览器地址栏中的pathname(location.pathname)
    <Link to='/first>页面一</Link>

Route组件: 指定路由展示组件相关信息

    //path属性:路由规则
    //component属性:展示的组件
    //Route组件写在哪,渲染出来的组件就展示在哪
    <Route path='/first component={First}></Route>

默认路由:path("/")

模糊匹配模式: 只要pathname以path开头就会匹配成功

10.png

11.png

12.png

精准匹配模式: 给React组件添加exact属性,让其变为精准匹配模式,只有path和pathname完全匹配时才会展示该路由

编程式导航: 通过JS代码实现,history是React提供用于获取浏览器历史记录的相关信息

  • push(path):跳转到某个页面,参数path表示要跳转的路径
  • go(n):前进或后退到某个页面,参数n表示前进或后退页面数量 案例:
import { BrowserRouter as RouterLinkRoute } from 'react-router-dom'
class Login extends React.Component {
  handleLogin = () => {
    //使用编程式导航实现路由跳转
    this.props.history.push('/home')
  }
  render() {
    return (
      <div className="login">
        <p>登录页面</p>
        <button onClick={this.handleLogin}>登录</button>
      </div>
    )
  }
}
const Home = (props) => {
  const handleBack = () => {
    props.history.go(-1)
  }
  return (
    <div>
      <h2>后台主页</h2>
      <button onClick={handleBack}>返回登录页</button>
    </div>
  )
}
class App extends React.Component {
  render() {
    return (
      <Router>
        <div>
          <h1>编程式导航</h1>
          <Link to='/login'>去登录页面</Link>
          <Route path='/login' component={Login} />
          <Route path='/home' component={Home} />
        </div>
      </Router>
    )
  }
}

react原理

setState

setState的更新是异步的

调用多次setState,只会触发一次render

    handleClick = () => {
      this.setState({
        count: this.state.count + 1 // 0 + 1
      })
      console.log('count:',this.state.count); // 0
      this.setState({
        count: this.state.count + 1 // 0 + 1
      })
      console.log('count:',this.state.count); // 0
    }
    render(){
        console.log('render')  
        return(
            ...      
        )
    }

另一种语法

    this.setState((state,props)=>{
      return{
          count:state.count + 1    
      }
    })

第二个参数,回调函数,拿到实时更新状态值

    this.setState(
      //updater
          (state, props) => {
            return {
              count: state.count + 1
            }
          },
     //callback
          ()=>{
            console.log('状态更新完成:',this.state.count); // 1
          }
    )
    console.log('状态更新前:',this.state.count); // 0

JSX语法

JSX是createElement()方法的语法糖

JSX语法被@babel/preset-react编译为createElement()方法

React元素:是一个对象,用来描述你希望在屏幕上看到的内容

13.png

React组件更新机制

父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树。

15.png

组件性能优化

  1. 减轻state,只存储和组件相关的数据,不用再渲染的数据不要放在state中
  2. 避免子组件没有任何变化时的重新渲染

解决办法:使用钩子函数shouldComponentUpdata(nextProps, nextState)

通过返回值决定是否重新渲染,返回true表示重新渲染,false表示不重新渲染

触发时间:组件重新渲染前执行(shouldComponentUpdata -> render)

案例:

class App extends React.Component {
    state = {
      count0
    }
    handleClick = () => {
      this.setState(() => {
        return {
          countMath.floor(Math.random() * 3)
        }
      })
    }
    shouldComponentUpdate(nextProps, nextState) {
      console.log('最新状态:',nextState,",当前状态:",this.state);
      if(nextState.count === this.state.count){
        return false
      }
      return true
    }
    render() {
      console.log('render');
      return (
        <div className='App' >
          <h1>随机数:{this.state.count} </h1>
          <button onClick={this.handleClick}>重新生成</button>
        </div>
      )
    }
  }

虚拟DOM和Diff算法

  1. 初次渲染时,React会根据初始state(Model),创建一个虚拟DOM对象

  2. 根据虚拟DOM生成真正的DOM,渲染到页面中

  3. 当数据变化后( setState() ),重新根据新的数据,创建新的虚拟DOM对象

  4. 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容

  5. 最后,React只将变化的内容更新(patch)到DOM中,重新渲染到页面

20.png

21.png