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
二、获取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
练习案例:发表评论,获取评论内容,点击发布,成功发布内容,清空内容并重新聚焦。里面还包含了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
三、组件通信
父传子
步骤: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
兄弟组件之间如果需要通信的话可以让父组件当中间的媒介。
使用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
四、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
空数组依赖:上述案例如果加上空数组,那么就只会在初始渲染时在控制台打印一次,值变化不会再打印。
特定依赖项:加上特定的值,初始执行一次,特定值变化再次执行。
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
注: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
使用规则
1.只能在组件中或者其他自定义Hook函数中调用
2.只能在组件的顶层调用,不能嵌套在if. for.其他函数中
六、useMemo
这个钩子函数用于计算值,并在依赖项改变时返回一个缓存的新结果,相当于vue中的watch。有两个参数,第一个参数就是要执行的回调函数,用于计算和返回值,第二个参数就是数组中的依赖项,用于监听改变的值。相关的使用例子就是对于中英文的切换,将全局中控制中英文的变量作为依赖项,回调函数里面判断使用中文包还是英文包。