React学习笔记

68 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

创建React项目

  • npm create-react-app name
  • vite 创建react项目

基础

JSX

js的语法拓展
class使用className 而不是class

    let element = <h1>this is jsx</h1>

JSX { } 包含js表达式

    let element = <h1>{{1+1}}</h1>
    //字符串字面量
    let ele1 = <div property="string"></div>
    //js表达式
    let ele2 = <img src={user.avatar}/>

组件

函数组件

    function Welcome(props){
        return (<img src={props.user.avatar}/>)
    }

class组件

(props 为this的属性)

    class Welcome extends react.Component{
        render(){
            return (<img src={this.props.user.avatar}/>)
        }
    }

props只读,不可改

state

直接修改state React 不会重新调用render()去更新Dom

// 使用setState() react能检测到state数据修改了,重新调用render渲染组件
    this.setState({
        user:user
    })

this.setState的更新可能是异步的
原因:出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。this.props 和 this.state 可能会异步更新

解决state异步更新导致状态更新不及时

setState()参数传入一个函数

//返回一个执行完成的结果对象
    this.setState(
            function(state,props){
            return {
                counter:state.counter+props.number;
            }
        }
    )

数据向下流动

通过props实现父组件对子组件的通信,数据由父组件流向子组件

生命周期

componentDidMount():组件完成挂载时,执行
componentWillUnmount():组件将要卸载时,执行

事件处理

this的指向问

回调函数中默认的this为undefined
一、在回调函数中让this指向component,用bind改变this的指向

    class Change extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                value:"this"
            }
            //bind后可以在 handleClick中使用this指向component
            this.handleClick = this.handleClick.bind(this);
        }
        handleClick(){
            this.setState({
                this:'hello world'
            })
        }
    }

二、使用class filed语法

class Change extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                value:"this"
            }
        }
        //class filed 语法
        handleClick = ()=>{
            this.setState({
                this:'hello world'
            })
        }
    }

三、使用箭头函数

    render(){
        return (
        //该方法每次render时都会重新创建匿名函数,不推荐使用
            <a onClick={()=>{this.handleClick}}></a>
        )
    }

事件处理函数传参

    //第一种方法(箭头函数)
     (<a onClick={(args)=>{this.handleClick(args)}}></a>);
     
     //第二种方法(bind)
     <a onclick={this.handleClikc.bind(this,args)}>

条件渲染

    //通过if实现 con1,con2分别为两个不同的组件
    if(render){
        return con1;
    }else{
        return con2;
    }
    //使用&&运算符实现条件渲染
    {
        //render = true 返回&&右边
        //render = false 跳过右边
        render&&(<h1> hello world</h1>)
    }
    //使用三目运算符实现条件渲染
    render?<h1>hello world</h1>:<h1>welcome React</h1>

隐藏组件

    class Header extends React.Component{
         constructor(props) {
            super(props);
         }
         render(){
            if(this.props.show){
              return (<h1>hello world</h1>)
            }
            else{
                //返回null 隐藏组件,页面结构仍然有header
              return null;
            }
      }
}

列表和key

和vue中的v-for和key
使用map来循环遍历

    let number = [
        {value:1,
         id:1
         },
         {value:2,
         id:2
         }
    ]
    //不建议使用index为key
    const element = number.map((item,index)=>{
        return <li key={item.id}>{item.value}</li>
    )
    //嵌入map式
    function List(props){
        return (
            <ul>{
                number.map((item,index)=>{
                    return <li key={item.id}>{item.value}</li>
                })
            }</ul>
        )
    }

受控组件

表单

通过令表单的value=this.state.value来实现表单和数据的绑定

   class NameForm extends React.Component{
      constructor(props){
        super(props);
        this.state = {
          name:''
        }
      }
      handleChange(event){
        this.setState({
          name:event.target.value
       })
      }
      render(){
        return (
        //通过onchang事件实现数据的改变
            <form>
              <input type='text' value={this.state.name} 
              onChange={this.handleChange.bind(this)}></input>
              <input type='submit' value="提交"></input>
            </form>
        )
      }
}

状态提升

多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去

组合

可以通过将组件以props参数的形式传入到另一个组件中去

    //组件1
    function FirstComponent (props){
        return (
            <div>
            {props.children}
            </div>
        )
    }
    //组件2
    function 

context

解决跨组件层级props转递效率问题

首先创建1个Context对象,当React渲染1个订阅了Context对象的组件, 这个组件会从组件树中离自身最近的那个匹配的Provider读取到当前的context值。 每个Context都返回1个React组件,允许消费组件订阅context的变化。 ContextType只能在类组件中使用, 1个组件如果有多个消费者,ContextType只对其中1个有效, 也就是ContextType只能有1个(所以推荐使用Provider和Consumer形式)

创建context

    const Mycontext = React.createContext(initalValue)

Provider 提供context

    let value = {...data}
    <Mycontext.Provider value = {value}>
    <\Mycontext.Provider>

消费context的三种方式

consumer组件,contextType,useContext

consumer组件

consumer组件内必须是一个函数

    <Mycontext.Consumer>
     {
         value =>(
             <div>{value.data}</div>
         )
     }
    <\Mycontext.Comsumer>

contextType

只能在类组件中使用 给类组件添加类变量contextType,通过实例变量this.context获取provider的value值

    class ...{
        static contextType = Mycontext;
        return (
            <div>{this.context.data}</div>
        )
    }

useContext

useContext hook 只能在函数组件中使用

    // value 即获取到Mycontext中provider生产的数据
    const value = useContext(Mycontext)

ref

用于标记dom元素,获取dom元素

使用方法

一、React.createRef()

    class RefText extends React.Component{
  constructor(){
    //创建一个ref,用来保存对dom节点的引用 目前current为null
    this.inputRef = React.createRef();
  }
  render(){
    return (
      //mounted后 this.inputRef.current指向该dom节点
      <input ref={this.inputRef}></input>
    )
  }
}

二、refs 回调函数 ref属性传入一个函数

    class RefTest extends React.Component{
  constructor(){
    this.testRef = null;
  }
  setRef = (el)=>{
    //testRef引用元素
    this.testRef = el;
  }
  render(){
    return (
      //若传入为props中的参数,可以暴露该dom给父组件
      <input ref={this.setRef}></input>
    )
  }
}

高阶组件

组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件 本质上是一个函数,处理加工一个组件,返回一个新的组件。

    //高阶组件
function withH(HComponent,data){
  //包装原组件,返回包装后的组件
  return class extends React.Component{
    constructor(props){
      super(props);
      this.state = {
        data:`${data}haha`
      }
    }
    render(){
      return (
        <HComponent Data={this.state.data}></HComponent>
      )
    }
  }
}
//原被包装组件
class Ha extends React.Component{
  constructor(props){
    super(props);
    this.data = this.props.Data;
  }
  render(){
    return (
      <h1>{this.data}</h1>
    )
  }
}
//调用函数生成新组件实例
const HaHa = withH(Ha,"nihao")

image.png

被包装组件接收来自容器组件的所有 prop,同时也接收一个新的用于 render 的 data prop

render props

render prop 是一个用于告知组件需要渲染什么内容的函数 prop

       /*render props*/
class AComponent extends React.Component{
  render(){
    return (<h2>渲染组件{this.props.mouse}</h2>)
  }
}
class Container extends React.Component{
  constructor(props){
    super(props);
  }
    render(){
      return(
        <div>
          <h1>Container</h1>
          {this.props.render("hello")}
        </div>
      )
    }
}class Finall extends React.Component{
    render(){
      return(
        <Container render={(mouse) => (
          <AComponent mouse={mouse} />
        )}/>
      )
    }
}

层级

image.png

Fragments

类似于vue中的template,有些许不同,Fragment可以包含多子个元素,无参数时:<></>

虚拟dom 和diff算法

react 路由

BrowserRouter 和 HashRouter

BrowserRouter使用的是h5的history API,不兼容ie9以下
HashRouter使用URL的hash值 路径表现形式不一样

  • BrowserRouter 的路径中没有 # ,如:localhost:3000/demo/test
  • HashRouter 的路径包含#,如:localhost:3000/#/demo/test

刷新后对路由 state 参数的影响

  • BrowserRouter 没有影响,因为 state 保存在 history 对象中。
  • HashRouter 刷新后会导致路由 state 参数的丢失!

备注:HashRouter 可以用于解决一些路径错误相关的[问题]

//react-route-dom6
    import {BrowserRouter,Routes,Route,Link} from 'react-router-dom'
    //BrowserRouter  所有路由都嵌套在其中(也可以是HashRouter)
    <BrowserRouter>
    //路由链接Link
      <nav }>
        <Link to="/"}>Home</Link>
        <Link to="/about"}>About</Link>
        <Link to="/post">Post</Link>
      </nav>
      //Routes 取代Router5中的 Switch
      <Routes>
          // Route 渲染组件的路由
          // element 指定组件(router5中的component)
        <Route path="/" element={<Home/>}/>
        <Route path="/about" element={<About/>}/>
        // 嵌套路由子级路由
        <Route path="/post" element={<Post/>}>
          <Route path='/post' element={<PostList/>} />
          //路由参数
          <Route path=':slug' element={<PostText/>} />
        </Route>
      </Routes>
   </BrowserRouter>
        <div>
            <h2>Blogs</h2>
            // 子级路由占位组件 Outlet
            <Outlet />
        </div>
        // useParams 获取路由传递的参数
          const {slug} = useParams();

antd (UI组件库)

引入

安装antd npm i antd --save
按需引入
import 模块引入

    import { Button } from 'antd';
    import 'antd/dist/antd.css';