React学习二:表单受控绑定、获取Dom、组件通信、useEffect、自定义Hook、useMemo

一、受控表单绑定

概念:使用react组件的状态(useState)控制表单的状态。双向绑定,数据变化视图也变,视图变数据也变。

import { useState } from 'react'

function App() {
  const [content, setContent] = useState('')
  return (
    <div>
      <input
            type='text'
            value={content}
            onChange={(e) => setContent(e.target.value)}
          </input>
    </div>
  )
}

export default App
View Code

二、获取DOM

在react组件中获取/操作dom,需要使用useRef钩子函数,分为两步。1、使用useRef创建ref对象,并于jsx绑定。2、DOM可用时,通过ref对象.current获取DOM对象(dom可用时就是dom渲染出来以后)。使用console.dir(),可以展开获取dom对象目录。

import { useState, useRef } from 'react'

function App() {
  const [content, setContent] = useState('')
  const inputRef = useRef(null)
  const handleClick = () => {
    console.log(inputRef.current)
  }
  return (
    <div>
      <input
        type='text'
        value={content}
        onChange={(e) => setContent(e.target.value)}
        ref={inputRef}>
      </input>
      <button onClick={handleClick}>点击</button>
    </div>
  )
}

export default App
View Code

练习案例:发表评论,获取评论内容,点击发布,成功发布内容,清空内容并重新聚焦。里面还包含了uuid和对时间格式化处理。

import { useState, useRef } from 'react'
import './index.css'
import { v4 as uuidv4 } from 'uuid' //https://github.com/uuidjs/uuid
import dayjs from 'dayjs' //https://dayjs.fenxianglu.cn/
function App() {
  const [commentList, setCommentList] = useState([
    {
      rpid: uuidv4(),
      content: '第一条评论',
      time: dayjs(new Date()).format('MM-DD hh:mm')
    }
  ])
  const [content, setContent] = useState('')
  const inputRef = useRef(null)
  const handlePublish = () => {
    setCommentList([
      ...commentList,
      {
        rpid: uuidv4(),
        content: content,
        time: dayjs(new Date()).format('MM-DD hh:mm')
      }
    ])
    setContent('')
    inputRef.current.focus()
  }
  return (
      <div>
        <div>
          <img src='/logo192.png' alt='logo' />
          <input
            type='text'
            value={content}
            onChange={(e) => setContent(e.target.value)}
            ref={inputRef}>
          </input>
          <button onClick={handlePublish}>发布</button>
        </div>
        <div>
          {commentList.map(item => <div key={item.rpid}>
            <span>{item.rpid}</span>
            <span>{item.content}</span>
            <span>{item.time}</span>
          </div>)}
        </div>
      </div>
  )
}

export default App
View Code

三、组件通信

父传子

步骤:1.父组件传递数据-在子组件标签上绑定属性2.子组件接收数据-子组件通过props参数接收数据。通过属性来传,数据的话种类很多都能传(对象,函数,html元素等)。

当我们把内容嵌套在子组件标签中时,子组件会自动在名为children的props属性中接收该内容。

数据单向流动,子组件中不能修改父组件中的值。

子传父

核心思路:调用父组件传来的函数来传递参数

import { useState } from 'react'
function Son({ name, onGetSonMsg }) {
  const message = 'hello world'
  return (
    <div>
      {name}
      <button onClick={() => onGetSonMsg(message)}>点击</button>
    </div>
  )
}
function App() {
  const [msg, setMsg] = useState('')
  const getMsg = (msg) => {
    console.log(msg);
    setMsg(msg)
  }
  return (
    <div>
      {msg}
      <Son name={'this is name'} onGetSonMsg={getMsg} />
    </div>
  )
}

export default App
View Code

兄弟组件之间如果需要通信的话可以让父组件当中间的媒介。

使用Content机制来实现跨层组件通信

步骤:1. createContext方法创建一个上下文对象2.在顶层组件通过Provider组件提供数据3.在底层组件通过useContext钩子函数使用数据

import { createContext, useContext } from 'react'
const MsgContext = createContext()
function Son() {
  return (
    <div>
      <GrandSon />
    </div>
  )
}
function GrandSon() {
  const m = useContext(MsgContext)
  return (
    <div>{m}</div>
  )
}
function App() {
  const Msg = 'hello 世界'
  return (
    <MsgContext.Provider value={Msg}>
      <div>
        <Son />
      </div>
    </MsgContext.Provider>
  )
}

export default App
View Code

四、useEffect

useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作,比如发送AJAX请求,更改DOM等等。useEffect副作用函数的执行时机有多种情况,根据传入的依赖项不同,有不同的执行表现。第一个参数是副作用函数,第二个参数是依赖项。

1、没有依赖项:组件初始渲染+组件更新时执行2、空数组依赖:只在初始渲染时执行一次3、添加特定依赖项:组件初始渲染+特性依赖项变化时执行

个人感觉初次渲染的时候非常像vue生命周期函数中的onMounted钩子函数,加特定依赖项的话就像是watch监听函数
没有依赖项案例,这里的组件更新是全局范围内的

import { useState, useEffect } from 'react'

function App() {
  const [count, setCount] = useState(0)
  useEffect(() => {
    console.log('useEffext被执行了');
  })
  return (
    <div>
      {count}
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

export default App
View Code

空数组依赖:上述案例如果加上空数组,那么就只会在初始渲染时在控制台打印一次,值变化不会再打印。

特定依赖项:加上特定的值,初始执行一次,特定值变化再次执行。

useEffect清除副作用,常见的时机是在组件卸载时自动完成。案例清除子组件中的定时器。

import { useState, useEffect } from 'react'
function Son() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('定时器执行中');
    }, 1000)
    return () => {
      clearInterval(timer)
    }
  }, [])
  return (
    <div>
      你好
    </div>
  )
}
function App() {
  const [show, setShow] = useState(true)
  return (
    <div>
      {show && <Son/>}
      <button onClick={() => setShow(false)}>移除Son组件</button>
    </div>
  )
}

export default App
View Code

注:useEffect如果依赖项是外面useState的一个变量,里面回调函数又是同一个useState的set函数,那么就会死循环;没有依赖项,useEffect回调函数里面有set函数,外面组件用到变量,这样也会死循环。useEffect里面可以访问外面useState的变量值,但是无法访问使用set函数修改后最新的变量值,用await阻断都不行。

五、自定义hook

概念:自定义Hook是以use打头的函数,通过自定义.Hook函数可以用来实现逻辑的封装和复用。封装思路:1.声明一个以use打头的函数2.在函数体内封装可复用的逻辑(只要是可复用的逻辑)3.把组件中用到的状态或者回调return出去(以对象或者数组)4.在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用。

import { useState } from 'react'
function useToggle() {
  const [value, setValue] = useState(true)
  const toggle = () => setValue(!value)
  return {
    value,
    toggle
  }
}
function App() {
  const { value, toggle } = useToggle()
  return (
    <div>
      {value && <div>this is div</div>}
      <button onClick={toggle}>toggle</button>
    </div>
  )
}

export default App
View Code

使用规则
1.只能在组件中或者其他自定义Hook函数中调用
2.只能在组件的顶层调用,不能嵌套在if. for.其他函数中

六、useMemo

这个钩子函数用于计算值,并在依赖项改变时返回一个缓存的新结果,相当于vue中的watch。有两个参数,第一个参数就是要执行的回调函数,用于计算和返回值,第二个参数就是数组中的依赖项,用于监听改变的值。相关的使用例子就是对于中英文的切换,将全局中控制中英文的变量作为依赖项,回调函数里面判断使用中文包还是英文包。

posted @ 2023-11-19 21:43  数星观月  阅读(187)  评论(0编辑  收藏  举报