React

122 阅读9分钟

JSX渲染数据

this.state = {
 hobby:['aaa','bbb','ccc']
}
render(){
    return(
        <ul>
             {this.state.hobby.map((item)=>{return(<li><span>{item}</span></li>)})}
        </ul>
    )   
}
// 解构
constuctor(){
  super()
  this.state = {
   name:'张三',
   age:12
  }
}
render(){
 const {name,age} = this.state  // 解构
 return (
  <div><h1>{name}</h1></div>
 )
}
// 绑定样式属性className
<div className="style"></div>
// 绑定style,需在{}中传入一个对象
<div style={{color:'red',fontSize:'20px'}}></div>

react中this指向

  • this指向:谁调用指向谁,箭头函数不绑定this,箭头函数的this指向父级 解决this的方案:
{/*1:构造函数绑定this*/}
constuctor(){
 super()
 this.handleChange = this.handleChange.bind(this)
}
<button onClick={this.handleChange()}></button>
handleChange(){}
{/*2:调用时绑定this*/}
constuctor(){
 super()
}
<button onClick={this.handleChange.bind(this)}></button>
handleChange(){}
{/*3:箭头函数*/}
constuctor(){
 super()
}
<button onClick={()=>this.handleChange()}></button>
handleChange(){} 
{/*4:箭头函数赋值*/}
constuctor(){
 super()
}
<button onClick={this.handleChange}></button>
handleChange = () =>{}

react修改数据

通过state定义的数据,必须通过setState修改数据,setState会触发render()

constuctor(){
 super()
 this.state = {
   name:'aaa'
 }
}
<h1>{this.state.name}</h1>  // bbbb
<button onClick={this.handleChange}></button>
handleChange = () =>{
  this.setState({
    name:'bbbb'
  })
}

类组件

import react,{Component} from 'react'
export default App extends Component{
  render(){return(<div></div>)}
}
  • 组件名称是大写字母开头
  • 类组件必须继承React.Component
  • 类组件必须实现render函数
  • constructor是可选的,初始化数据,绑定this
  • this.state是状态,维护组件中内部的数据
  • render方法必须返回一个jsx元素

函数组件

import React from 'react'
function App(){return (<div></div>)}
export default App
  • 没有生命周期
  • 没有this
  • 没有状态

组件的生命周期

  • 挂载阶段:组件第一次在DOM树中被渲染的过程
  • 更新阶段:组件状态肤色很难过变化,重新更新渲染的过程
  • 卸载阶段:组件从DOM树中被移除的过程

组件传值,属性的校验

import PropTypes from 'prop-types'
class Father extends Component {
    render(){
    //传递一个对象
    const car = {brand:'bmw',color:'white'}
      return (
       <div>
         <Son money={100} wealth={['a','b','c']} {...car}/>
        </div>
       )
    }
}
class Son extends Component {
    constuctor(props){
     super(props)
    }
    render(){
      return (<div><h1>{this.props.money}<h1/></div>)
    }
}
//子组件添加一个propTypes属性
Son.propTypes = {money:PropTypes.number.isRequired}

父传子

  • 父组件提供要传递的数据
  • 给子组件标签添加属性,属性值为我们要传递的数据
  • 子组件通过props接收父组件中传递的数据

子传父

思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数

  • 父组件提供一个函数(用于接收数据)
  • 将这个函数作为属性的值,传递给子组件
  • 子组件通过props调用父组件的函数
  • 将子组件中的数据作为参数传递给父组件的函数

跨级组件传

  • 组件层次在3层内,使用props一层层的传递
  • context对象,context相当于一个全局变量,不管嵌套有多深,可以随意地使用
  • redux

组件的优化

第一种:避免组件的重复渲染

// 第一种
比较新旧的props,决定是否重新渲染组件,不相等,会重新渲染
shouldComponentUpdate(nextProps,nextState){}
// 第二种 
PureComponent
浅比较props和state,(shallowEqual)实现的shouldComponentUpdate,
不会进行深层次的比较,只比较外层数据结构,只要外层相同,则认为没有变化。

PureComponent存在的问题:
- 函数式组件不可以用
- 页面进行浅比较,可能由于深层数据的不一致导致而产生错误判断,导致页面的不更新
- 不适合在多层次的嵌套对象

key

不建议使用index作为key的值

setState

setState是同步还是异步?

react18之前:

  • 1:在合成事件中是异步的,在生命周期钩子中是异步的
  • 2:react17中,在原生事件、定时器中是同步的 合成事件:react内部的一套事件处理机制,不是原生事件

同步更新:每更新state就执行一次render 全部更新完毕以后执行一次render(异步)

react18,实现自动批量更新,react将多次state更新进行合并处理,最终只进行一次渲染。[ReactDOM.createRoot]

react17,用reactDOM.unstable_batchedUpdates()方法实现手动批量更新[ReactDOM.render]

拷贝数据

ref 用于访问在render方法中创建的React元素,获取DOM,进行操作

  • 用在组件上,就是组件实例
  • 用在标签元素,获取的就是DOM元素

受控组件/非受控组件

受控组件、非受控组件指的都是表单元素:<input/><textarea/><select/> 受控组件,表单元素受react组件管理控制,表单的状态(数据)修改只能通过setState()更新 特点:数据可控,完全由react中的state来管理 非受控组件:使用Ref从DOM节点获取表单数据,表单数据交由DOM节点处理 特点:ref获取DOM节点进行数据更新

高阶组件

高阶函数:一个函数可以接收另外一个函数作为参数

  • 接收一个或多个函数作为输入
  • 返回一个函数

高阶组件是一个函数,参数是一个组件,返回一个组件

高阶组件通过props把参数传递给目标组件,目标组件就直接从props里拿数据

高阶组件是react中用于复用组件的逻辑的一种技巧,HOC不是react api的一部分,是一种基于react的组合特性而形成的设计模式

高阶组件就是参数是组件,返回值是一个组件,高阶组件就是一个函数

高阶组件解决的是功能的复用,不是代码的复用

高阶组件的主要功能是封装并分离组件的通用逻辑,让通用的逻辑在组件间更好的复用

hooks

// 函数式编程
import react from 'react'
const App = ()=>{return <div></div>}
export default App

函数式编程: 纯函数:就是一个函数,一个函数的返回结果只依赖自己的参数,并且执行的过程中没有副作用 副作用:除了干自己的主要任务外,还做了其他事情 当一个函数调用时,除了返回函数值以外,还对主调用函数产生附加的影响 组件尽量写成纯函数,如果需要外部的数据或者是功能,准备一钩子,把涉及到的数据或者是功能的代码钩进纯函数组件里

useState:声明状态变量

返回一个数组 第一个元素是当前的状态 第二个元素是一个函数,这个函数的作用是设置修改状态

// 声明一个state的变量,赋值初始化 0 只在首次渲染的时候使用,如果去更新的话,这个0 就会被忽略
// 可以多次调用,每次都是独立的
// 只可以在函数组件内使用
const [xxx,setXxx] = useState(0)
使用immer可以修改state
import produce from 'immer'

useEffect 可以为react组件完成副作用的操作

react中,一个纯函数式组件主要的功能是通过数据渲染UI,剩余的其他操作都是副作用 - ajax请求 - 手动DOM - 本地存储

// 每次都会执行
useEffect(()=>{])
// [] 依赖空数组,由于依赖为空,不会变化,只执行一次
useEffect(()=>{},[])

useEffect的第二个参数,是依赖的变量,是一个数组,可以有多个依赖项,一旦这一个依赖项发生变动,useEffect就会重新执行

依赖项执行机制:

1:默认状态:首次执行+每次组件更新都执行 2:添加一个空的[]:首次渲染时执行,执行一次 3:添加特定的依赖项:首次执行 + 依赖项发生更新时执行useEffect()函数

// ajax请求
const [list,setList] = useState([])
useEffect(()=>{
  const initData = async()=>{
  try{
      const {data} = await axios.get('')
        setList(data.data)
      } catch(err){}
      }
      initData()   
},[])
// 清除定时器
const[count,setCount] = useState(0)
useEffect(()=>{
 const timer = setInterval(()=>{
  setCount(count => count + 1)
 })
 // 清除定时器 return是effect可选的清除机制 每一个effect都可以返回一个清除函数
 // return 是在组件被销毁之前才会执行
 return () =>{
 clearInterval(timer)
 }
},[])

useContext

帮助我们跨组件通信

使用步骤: 1:使用createContext创建context对象 2:在顶层组件通过Provider提供数据 3:底层组件通过useContext(context)函数获取数据

import React,{useContext,createContext} from 'react'
const context = createContext()

function Grandson(){
 const {count,setCount} = useContext(context)
 return (
  <div>
   <span>{count}</sapn>
   <button onClick={()=>setCount(count + 1)}>加1</button>
  </div>
 )
}

function Son(){
    return (
     <div><Grandson/></div>
    )
}
function Father(){
  const [count,setCount] = useState(0)
  return (
    <div>
     <context.Provider value ={{count,setCount}}>
      <Son/>
     </context.Provider>
    </div>
  )
}
export default function App(){
  return(
   <div><Father/></div>
  )
}

useRef

主要功能是帮助我们获取DOM元素或者组件,还尅保存值

1:引入useRef,调用useRef() 2:通过ref属性将useRef()的返回值绑定到元素上 3:useRef()返回的是一个对象,对象内有一个current属性

useReducer

useState提供组件状态的,useReducer是useState升级版,所有的useState规则,useReducer都适用

1:定义一个initState 2: 定义一个reducerh函数,把所有操作方法都放到这个函数里边 3:把initState跟reducer,通过的是useReducer关联起来,返回一个当前的state和dispatch 4:当需要计算时,使用dispatch传递一个action值,触发的reducer函数,返回一个新的state

useCallback

解决重复渲染问题,性能优化

useCallback起到一个缓存的作用,即便父组件重新渲染,useCallback包裹的函数不会重新生成,会返回上一次函数的引用

useMemo

父组件渲染,会生成一个新的对象,导致传递给子组件的person属性发生了变化,进而会导致子组件重新渲染,浅比较

第一个参数:是一个函数,返回的对象的指向同一个引用,不会创建新的对象 第二个参数是一个数组,只有数组的变量改变时,第一个参数的函数才会返回一个新的对象

高阶组件使用场景

高阶组件就是一个函数,这个函数接收一个组件作为参数,并且返回一个新的组件。 HOC的作用: 1:代码复用 2:增强props(不需要每一个组件都传递新增的属性,使用高阶组件传递可以给每一个组件新增props)

存在的缺陷:需要在原组件上包裹或者嵌套,如果存在大量的HOC,会影响性能

Portal

Portal提供了一种将子节点渲染到存在于父组件以外的DOM节点的方案

Portal可以让节点渲染到root节点以外的节点

悬浮框,提示框,对话框

prop.children

每一个组件都有props.children属性,通过这个可以方便的拿到内存元素,props.children表示组件的所有子节点

fragment

定义组件时,最外层的div不想被渲染时,可以使用