React基础学习笔记

2 阅读17分钟

react 学习笔记

1. react概念

React是什么

一个专注于构建用户界面的 JavaScript 库,和vue和angular并称前端三大框架,不夸张的说,react引领了很多新思想,世界范围内是最流行的js前端框架,最近发布了18版本,加入了很多新特性

React英文文档(reactjs.org/

React中文文档 (zh-hans.reactjs.org/

React新文档 (beta.reactjs.org/)(开发中....)

React有什么特点

1- 声明式UI(JSX)

写UI就和写普通的HTML一样,抛弃命令式的繁琐实现

 const VDOM = <h1>Hello React</h1>

2- 组件化

组件是react中最重要的内容,组件可以通过搭积木的方式拼成一个完整的页面,通过组件的抽象可以增加复用能力和提高可维护性

3- 跨平台

react既可以开发web应用也可以使用同样的语法开发原生应用(react-native),比如安卓和ios应用,甚至可以使用react开发VR应用,想象力空间十足,react更像是一个 元框架 为各种领域赋能

2. 创建React项目

  1. 在网站中添加 React

    1. 引入react核心库js文件个和babel文件

       <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> 
       <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
       ​
       <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
      
    2. 创建容器和渲染

       <div id="root"></div>   
      
        const root = ReactDOM.createRoot(document.querySelector('#test'))
        root.render(<Login></Login>)
      

      虚拟DOM创建

      1. jsx方式

          const root = ReactDOM.createRoot(document.querySelector("#test"));
          root.render(VDOM)
        
      2. js

          //1.创建虚拟DOM
          const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
          //2.渲染虚拟DOM到页面
          ReactDOM.render(VDOM,document.getElementById('test'))
        
    3. 完整结构

       <!DOCTYPE html>
       <html>
       ​
       <head>
         <meta charset="UTF-8" />
         <title>Learn React</title>
       </head>
       ​
       <body>
         <div id="test"></div>
       ​
         <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
         <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
         <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
         <script type="text/babel">
           
           //  1. 创建jsx结构
           const VDOM = <h1>Hello React!!!</h1>
           //2.渲染虚拟DOM到页面 18版本写法
           const root = ReactDOM.createRoot(document.querySelector('#test'))
           root.render(VDOM)
           // 低版本写法,
           // ReactDOM.render(VDOM, document.getElementById("test"));
         </script>
       </body>
       ​
       </html>
      
  2. 脚手架创建

     npx create-react-app xxxx 
     //注意项目名都只能小写
    

    项目目录说明调整

    • 目录说明

      1. src 目录是我们写代码进行项目开发的目录

      2. package.json 中俩个核心库:react 、react-dom

    • 目录调整

      1. 删除src目录下自带的所有文件,只保留app.js根组件和index.js

      2. 创建index.js文件作为项目的入口文件,在这个文件中书写react代码即可

    • 入口文件说明

      import React from 'react'
      import ReactDOM from 'react-dom'
      import './index.css'
      // 引入根组件App
      import App from './App'
      // 通过调用ReactDOM的render方法渲染App根组件到id为root的dom节点上
      // React.StrictMode 是开启严格模式
      ReactDOM.render(
        <React.StrictMode>
          <App />
        </React.StrictMode>,
        document.getElementById('root')
      )
      

3. JSX 基础

1. JSX介绍

目标任务: 能够理解什么是JSX,JSX的底层是什么

概念:JSX是 JavaScript XML(HTML)的缩写,表示在 JS 代码中书写 HTML 结构

作用:在React中创建HTML结构(页面UI结构)

优势

  1. 采用类似于HTML的语法,降低学习成本,会HTML就会JSX
  2. 充分利用JS自身的可编程能力创建HTML结构

注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法。

语法规则

  1. 定义虚拟DOM时,不要写引号。

  2. 标签中混入JS表达式时要用 {}

  3. 样式的类名指定不要用class,要用className

  4. 内联样式,要用style={{key:value}} 的形式去写。

  5. 只有一个根标签

  6. 标签必须闭合

  7. 标签首字母

    (1). 若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。

    (2). 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。

区分表达式和语句

一定注意区分:【js语句(代码)】与【js表达式】

1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方

下面这些都是表达式:

(1). a

(2). a+b

(3). demo(1)

(4). arr.map()

(5). function test () {}

2.语句(代码):

下面这些都是语句(代码):

(1).if(){}

(2).for(){}

(3).switch(){case:xxxx}

2. JSX使用js表达式

语法

{ JS 表达式 }

可以使用的表达式

  1. 字符串、数值、布尔值、null、undefined、object( [] / {} )
  2. 1 + 2、'abc'.split('')、['a', 'b'].join('-')
  3. fn()

3. JSX列表渲染

实现:使用map函数

// 来个列表
const songs = [
  { id: 1, name: '痴心绝对' },
  { id: 2, name: '像我这样的人' },
  { id: 3, name: '南山南' }
]

function App() {
  return (
    <div className="App">
      <ul>
        {
          songs.map((item,inedx) => <li key={index}>{item.name}</li>)
        }
      </ul>
    </div>
  )
}

export default App

注意点:需要为遍历项添加key值,一般展示可以使用index,否则为了效率,最好使用唯一标识(id等)

  1. key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用
  2. key 在当前列表中要唯一的字符串或者数值(String/Number)
  3. 如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值
  4. 如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值

4. JSX条件渲染

目标任务: 能够在JSX中实现条件渲染

作用:根据是否满足条件生成HTML结构,比如Loading效果

实现:可以使用 三元运算符逻辑与(&&)运算符

// 来个布尔值
const flag = true
function App() {
  return (
    <div className="App">
      {/* 条件渲染字符串 */}
      {flag ? 'react真有趣' : 'vue真有趣'}
      {/* 条件渲染标签/组件 */}
      {flag ? <span>this is span</span> : null}
    </div>
  )
}
export default App

5. JSX 样式

目标任务: 能够在JSX中实现css样式处理

  • 行内样式 - style

    <div style={{ color: 'red' }}>this is a div</div>   
    
  • 行内样式 - style - 更优写法

    const styleObj = {
        color:red
    }
    <div style={ styleObj }>this is a div</div> 
    
  • 类名-className-动态类名控制

    import './app.css'
    function App() {
      const [showTitle,setShowTitle] = useState(true)
      return (
        <div className="App">
          <div className={ showTitle ? 'title' : ''}>this is a div</div>
        </div>
      )
    }
    export default App
    

4. 函数式组件和类组件

  1. 函数式组件

     //1.创建函数式组件
     function MyComponent(){
         console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
         return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
     }
     //2.渲染组件到页面
     ReactDOM.render(<MyComponent/>,document.getElementById('test'))
    

    执行了ReactDOM.render(.......之后,发生了什么?

    1. React解析组件标签,找到了MyComponent组件。
    2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
  2. 类组件

<script type="text/babel">
		//1.创建组件
		class Weather extends React.Component{
			
			//构造器调用几次? ———— 1次
			constructor(props){
				console.log('constructor');
				super(props)
				//初始化状态
				this.state = {isHot:false,wind:'微风'}
				//解决changeWeather中this指向问题
				this.changeWeather = this.changeWeather.bind(this)
			}

			//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
			render(){
				console.log('render');
				//读取状态
				const {isHot,wind} = this.state
				return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
			}

			//changeWeather调用几次? ———— 点几次调几次
			changeWeather(){
				//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
				//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
				//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
				
				console.log('changeWeather');
				//获取原来的isHot值
				const isHot = this.state.isHot
				//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
				this.setState({isHot:!isHot})
				console.log(this);

				//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
				//this.state.isHot = !isHot //这是错误的写法
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Weather/>,document.getElementById('test'))
				
	</script>

5. 事件处理

1. 如何绑定事件

  • 语法 on + 事件名称 = { 事件处理程序 } ,比如:<div onClick={ onClick }></div>

    react会自动传递事件对象参数event

  • 注意点

    • react事件采用驼峰命名法,比如:onMouseEnter、onFocus
    • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
    • 通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
  • 样例

// 函数组件
function HelloFn () {
  // 定义事件回调函数
  const clickHandler = (event) => {
    console.log('事件被触发了',event)
  }
  return (
    // 绑定事件
    <button onClick={clickHandler}>click me!</button>
  )
}

2. 传递额外参数

改造事件绑定为箭头函数 在箭头函数中完成参数的传递

return (
      <ul>
        {list.map(item =>(
           <li key={item.id}>
                {item.name}
                <button onClick={(e) => onDel(e, item.id)}>x</button>
           </li>
        ))}
      </ul>
  )

3. 阻止默认事件

在React中,无法通过return false取消默认行为

  • return false;

事件对象

    • React事件中同样会传递事件对象,可以在响应函数中定义参数来接收
    • React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
    • 由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题
const clickHandler = (event) => {
     event.preventDefault(); // 取消默认行为
     event.stopPropagation(); // 取消事件的冒泡

     alert('我是App中的clickHandler!');
};

6. state

state * state实际就是一个被React管理的变量 * 当我们通过setState()修改变量的值时,会触发组件的自动重新渲染

* 只有state值发生变化时,组件才会重新渲染

* 当state的值是一个对象时,修改时是使用新的对象去替换已有对象

* 当通过setState去修改一个state时,并不表示修改当前的state

  *         它修改的是组件下一次渲染时state值

* setState()会触发组件的重新渲染,它是**异步的**

* 所以当调用setState()需要用旧state的值时,一定要注意

* 有可能出现计算错误的情况

* 为了避免这种情况,可以通过为setState()传递回调函数的形式来修改state值

  ```jsx
   setCounter((prevCounter)=>{
                  /*
                  *   setState()中回调函数的返回值将会成为新的state值
                  *       回调函数执行时,React会将最新的state值作为参数传递
                  * */
                  return prevCounter + 1;
              });
  ```

7.Ref

何时使用 Refs

下面是几个适合使用 refs 的情况:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

使用Refs:

  1. 字符串refs (不推荐)

    <input ref="input1" type="text" placeholder="点击按钮提示数据"/>
    
    //获取
    this.refs.input1
    
  2. 回调函数形式refs

    <input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据"/>
    //获取
     showData2 = () => {
    			const {input2} = this
                alert(input2.value)
            }
    
  3. createRef-API

  4. createRef

const myRef = React.createRef()
<input ref={myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;

8. 组件之间通信

  1. props ---- 父=》子

    1. props可以传递任意数据

      数字、字符串、布尔值、数组、对象、函数、JSX

  2. 子=》 父

    父组件给子组件传递回调函数,子组件调用

  3. 兄弟组件通信--状态提升机制(将数据给共同的父组件保管)

  4. 跨组件通信Context

    1. 实现步骤

      1- 创建Context对象 导出 Provider 和 Consumer对象

      const { Provider, Consumer } = createContext()
      

      2- 使用Provider包裹上层组件提供数据

      <Provider value={xxx}>
          {/* 根组件 */}
      </Provider>
      

      3- 需要用到数据的组件使用Consumer包裹获取数据

      <Consumer >
          {value => /* 基于 context 值进行渲染*/}
      </Consumer>
      
    2. 完整代码

      import React, { createContext }  from 'react'
      
      // 1. 创建Context对象 
      const { Provider, Consumer } = createContext()
      
      
      // 3. 消费数据
      function ComC() {
        return (
          <Consumer >
            {value => <div>{value}</div>}
          </Consumer>
        )
      }
      
      function ComA() {
        return (
          <ComC/>
        )
      }
      
      // 2. 提供数据
      class App extends React.Component {
        state = {
          message: 'this is message'
        }
        render() {
          return (
            <Provider value={this.state.message}>
              <div className="app">
                <ComA />
              </div>
            </Provider>
          )
        }
      }
      
      export default App
      

props校验规则

目标任务: 掌握组件props的校验写法,增加组件的健壮性

对于组件来说,props是由外部传入的,我们其实无法保证组件使用者传入了什么格式的数据,如果传入的数据格式不对,就有可能会导致组件内部错误,有一个点很关键 - 组件的使用者可能报错了也不知道为什么,看下面的例子

实现步骤

  1. 安装属性校验包:yarn add prop-types
  2. 导入prop-types
  3. 使用 组件名.propTypes = {} 给组件添加校验规则
import PropTypes from 'prop-types'

const List = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
  return <ul>{lis}</ul>
}

List.propTypes = {
  colors: PropTypes.array
}

props校验-规则说明

目标任务: 掌握props常见的规则

四种常见结构

  1. 常见类型:array、bool、func、number、object、string
  2. React元素类型:element
  3. 必填项:isRequired
  4. 特定的结构对象:shape({})
// 常见类型
optionalFunc: PropTypes.func,
// 必填 只需要在类型后面串联一个isRequired
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
	color: PropTypes.string,
	fontSize: PropTypes.number
})

Hooks

1. 什么是hooks

Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”

2. Hooks解决了什么问题

Hooks的出现解决了俩个问题 1. 组件的状态逻辑复用 2.class组件自身的问题

  1. 组件的逻辑复用 在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式 但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等
  2. class组件自身的问题 class组件就像一个厚重的‘战舰’ 一样,大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的'快艇'

3. useState

作用

useState为函数组件提供状态(state)

使用步骤

  1. 导入 useState 函数
  2. 调用 useState 函数,并传入状态的初始值
  3. useState函数的返回值中,拿到状态和修改状态的方法
  4. 在JSX中展示状态
  5. 调用修改状态的方法更新状态
import { useState } from 'react'

function App() {
  // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0
  // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)
  const [count, setCount] = useState(0)
  return (
    <button onClick={() => { setCount(count + 1) }}>{count}</button>
  )
}
export default App

注意:useState 的初始值(参数)只会在组件第一次渲染时生效。也就是说,以后的每次渲染,useState 获取到都是最新的状态值,React 组件会记住每次最新的状态值

4. useEffect

1.什么是副作用

副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)

常见的副作用

  1. 数据请求 ajax发送
  2. 手动修改dom
  3. localstorage操作

useEffect函数的作用就是为react函数组件提供副作用处理的!

  1. 报错为:Too many re-renders.

  2. 当我们直接在函数体中调用setState时,就会触发上述错误

  3. ——问题:

    1. 不是说过,当新的state值和旧值相同时,它是不会触发组件的重新渲染的

    2. ——setState()的执行流程(函数组件)

    3. setCount() --> dispatchSetDate() --> 会先判断,组件当前处于什么阶段

    -如果是渲染阶段 --> 不会检查state值是否相同
    
    -如果不是渲染阶段 --> 会检查state的值是否相同
    
        -如果值不相同,则对组件进行重新渲染
    
        -如果值相同,则不对组件进行重新渲染
    
            -如果值相同,React在一些情况下会继续执行当前组件的渲染
    
            -但是这个渲染不会触发其子组件的渲染,这次渲染不会产生实际的效果
    
            -这种情况通常发生在值第一次相同时
    

2. 基础使用

useEffect 是在组件渲染完毕后执行

  1. 导入 useEffect 函数
  2. 调用 useEffect 函数,并传入回调函数
  3. 在回调函数中编写副作用处理(dom操作)
  4. 修改数据状态
  5. 检测副作用是否生效
import { useEffect, useState } from 'react'

function App() {
  const [count, setCount] = useState(0)
 
  useEffect(()=>{
    // dom操作
    document.title = `当前已点击了${count}次`
  })
  return (
    <button onClick={() => { setCount(count + 1) }}>{count}</button>
  )
}

export default App

3. 依赖项控制执行时机

在useEffect()可以传递一个第二个参数,

第二个参数是一个数组,在数组中可以指定Effect的依赖项

  • 指定后,只有当依赖发生变化时,Effect才会被触发
  • 通常会将Effect中使用的所有的局部变量都设置为依赖项

不添加依赖项

组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行

  1. 组件初始渲染
  2. 组件更新 (不管是哪个状态引起的更新)
useEffect(()=>{
    console.log('副作用执行了')
})

添加空数组

组件只在首次渲染时执行一次

useEffect(()=>{
    console.log('副作用执行了')
},[])

添加特定依赖项

副作用函数在首次渲染时执行,在依赖项发生变化时重新执行

function App() {  
    const [count, setCount] = useState(0)  
    const [name, setName] = useState('zs') 
    
    //只会在首次执行和count和name值改变时执行
    useEffect(() => {    
        console.log('副作用执行了')  
    }, [count,name])  
    
    return (    
        <>      
         <button onClick={() => { setCount(count + 1) }}>{count}</button>      
         <button onClick={() => { setName('cp') }}>{name}</button>    
        </>  
    )
}

注意事项

useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项就会有bug

4. 清理副作用

如果想要清理副作用 可以在副作用函数中的末尾return一个新的函数,在新的函数中编写清理副作用的逻辑

注意执行时机为:

  1. 组件卸载时自动执行
  2. 组件更新时,下一个useEffect副作用函数执行之前自动执行
import { useEffect, useState } from "react"


const App = () => {
  const [count, setCount] = useState(0)
  useEffect(() => {
    const timerId = setInterval(() => {
      setCount(count + 1)
    }, 1000)
    return () => {
      // 用来清理副作用的事情
      clearInterval(timerId)
    }
  }, [count])
  return (
    <div>
      {count}
    </div>
  )
}

export default App

5. useContext

子组件不用包裹,直接使用useContext接受。

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

6. useReducer

  1. 基础介绍

    // useReducer(reducer, initialArg, init)

    参数

    reducer : 整合函数

    对于我们当前state的所有操作都应该在该函数中定义

    该函数的返回值,会成为state的新值

    reducer在执行时,会收到两个参数:

    state 当前最新的state

    action 它需要一个对象

    在对象中会存储dispatch所发送的指令

    initialArg : state的初始值,作用和useState()中的值是一样

    返回值

    数组:

    第一个参数,state 用来获取state的值

    第二个参数,state 修改的派发器

    通过派发器可以发送操作state的命令

    具体的修改行为将会由另外一个函数(reducer)执行

  2. 使用

    import React, {useReducer, useState} from 'react';
    
    
    // 为了避免reducer会重复创建,通常reducer会定义到组件的外部
    const countReducer = (state, action) => {
        switch (action.type) {
            case 'ADD':
                return state + 1;
            case 'SUB':
                return state - 1;
            default:
                return state;
        }
    };
    
    const App = () => {
        	// 模式使用方式	
            // const [count, setCount] = useState(1);
            //
            // const addHandler = () => {
            //     setCount(prevState => prevState + 1);
            // };
            //
            // const subHandler = () => {
            //     setCount(prevState => prevState - 1);
            // };
    
            const [count, countDispatch] = useReducer(countReducer, 1);
    
            const addHandler = () => {
                // 增加count的值
                countDispatch({type: 'ADD'});
            };
    
            const subHandler = () => {
                // 增加count的值
                countDispatch({type: 'SUB'});
            };
    
            return (
                <div style={{fontSize: 30, width: 200, height: 200, margin: '100px auto', textAlign: 'center'}}>
                    <button onClick={subHandler}>减少</button>
                    {count}
                    <button onClick={addHandler}>增加</button>
    
                </div>
            );
        }
    ;
    
    export default App;
    

7. react-Memo

React.memo() 是一个高阶组件

它接收另一个组件作为参数,并且会返回一个包装过的新组件

包装过的新组件就会具有缓存功能,

包装过后,只有组件的props发生变化化

才会触发组件的重新的渲染,否则总是返回缓存中结果

B.js

import React from 'react';

const B = (props) => {
    console.log('B渲染');
    return (
        <div>
            <h2>组件B</h2>
            <p>{props.test && '哈哈'}</p>
        </div>
    );
};
export default React.memo(B);

A.js

import React, {useState} from 'react';
import B from "./B";

const A = () => {
    console.log('A渲染');
    const [count, setCount] = useState(1);

    const clickHandler = () => {
      setCount(prevState => prevState + 1);
    };

    const test = count % 4 === 0;

    return (
        <div>
            <h2>组件A -- {count}</h2>
            <button onClick={clickHandler}>增加</button>
            <B test={test}/>
        </div>
    );
};

export default React.memo(A);

App.js

import React, {useState} from 'react';
import A from "./components/A";

const App = () => {
    console.log('App渲染');
    const [count, setCount] = useState(1);

    const clickHandler = () => {
        setCount(prevState => prevState + 1);
    };

    return (
        <div>
            <h2>App -- {count}</h2>
            <button onClick={clickHandler}>增加</button>

            <A/>
        </div>
    );
};

export default App;

8. useCallback

useCallback 是一个钩子函数,用来创建React中的回调函数 ​ useCallback 创建的回调函数不会总在组件重新渲染时重新创建

useCallback()

参数:

  1. 回调函数

  2. 依赖数组

    • 当依赖数组中的变量发生变化时,回调函数才会重新创建
    • 如果不指定依赖数组,回调函数每次都会重新创建
    • 一定要将回调函数中使用到的所有变量都设置到依赖数组中
    • 除了(setState)
import React, {useCallback, useState} from 'react';
import A from "./components/A";

const App = () => {
    console.log('App渲染');
    const [count, setCount] = useState(1);
    const [num, setNum] = useState(1);
    const clickHandler = useCallback(() => {
        setCount(prevState => prevState + num);
        setNum(prevState => num + 1);
    }, [num]);

    return (
        <div>
            <h2>App -- {count}</h2>
            <button onClick={clickHandler}>增加</button>
            <A onAdd={clickHandler}/>
        </div>
    );
};

export default App;

9. useRef

获取原生的DOM对象

  1. 可以使用传统的document来对DOM进行操作

2 直接从React处获取DOM对象 步骤: 1. 创建一个存储DOM对象的容器

````
1. 使用 **useRef()** 钩子函数
      钩子函数的注意事项:
          ① React中的钩子函数只能用于函数组件或自定义钩子
          ② 钩子函数只能直接在函数组件中调用

2. 将容器设置为想要获取DOM对象元素的ref属性

   ```jsx
   <h1 ref={xxx}>....</h1>
   ```
````

React会自动将当前元素的DOM对象,设置为容器current属性

useRef()

  • 返回的就是一个普通的JS对象 {current:undefined}

  • 所以我们直接创建一个js对象,也可以代替useRef()

    const h1Ref = {current:null};
    
  • 区别

    • 我们创建的对象,组件每次重新渲染都会创建一个新对象 useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象
    • 当你需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()