参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过计算才能获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用
const [name, setName] = useState(()=>{ // 编写计算逻辑 return '计算之后的初始值'})
name
的初始值语法选择
useState(普通数据)
即可useState(()=>{})
eg。
- import { useState } from 'react'
-
- // 计算的含义
- function getDefaultValue(){
- for(let i =0 ;i<1000;i++){
- }
- return '10'
- }
-
- function Counter(props) {
- const [count, setCount] = useState(() => {
- // 这里目的:为了体现初始值经过一定的计算
- // 这个计算比较广义的概念
- // 只要无法直接确定,需要通过一定的操作才能获取,
- // 就可以理解为计算
- // 循环遍历一万条数据,才能确定这里的初始值是什么
- return props.count
- ------------------------
- return getDefaultValue()
- })
- return (
- <div>
- <button onClick={() => setCount(count + 1)}>{count}button>
- div>
- )
- }
-
- function App() {
- return (
- <>
- <Counter count={10} />
- <Counter count={20} />
- >
- )
- }
-
- export default App
有一些副作用是需要清除的,可以防止引起内存泄露。
在组件被销毁时,如果有些副作用操作需要被清理,就可以使用此语法,比如常见的定时器
- useEffect(() => {
- console.log('副作用函数执行了')
- // 副作用函数的执行时机为: 在下一次副作用函数执行之前执行
- return () => {
- console.log('清理副作用的函数执行了')
- // 在这里写清理副作用的代码
- }
- })
- import { useEffect, useState } from 'react'
-
- function Test () {
- //清除副作用effect
- useEffect(() => {
- let timer = setInterval(() => {
- console.log('定时器执行了')
- }, 1000)
- return () => {//执行时机:React将会在执行清除操作时调用它--componentWillMount
- //清理的动作
- clearInterval(timer)
- }
- }, [])
- return (
- <div>this is testdiv>
- )
- }
-
- function App () {
- const [flag, setFlag] = useState(true)
- return (
- <>
- {flag ? <Test /> : null}
- <button onClick={() => setFlag(!flag)}>switchbutton>
- >
- )}
将setFlag作为函数书写的案例:
- import { useEffect, useState } from 'react'
- function A () {
- useEffect(() => {
- let timer = setInterval(() => {
- console.log(111)
- }, 1000)
- return () => {
- clearInterval(timer)
- }
- })
-
- return (
- <div>
- A组件
- div>
-
- )
- }
- function App () {
- const [isShow, setIsShow] = useState(true)
- function changeA () {
- setIsShow(false)
- }
- return (
- <div>
- {isShow ? <A /> : null}
- <button onClick={changeA}>销毁A组件button>
- div>
- )}
添加副作用函数前:组件虽然已经不显示了,但是定时器依旧在运行
- import { useEffect, useState } from 'react'
- function Foo() {
- useEffect(() => {
- setInterval(() => {
- console.log('副作用函数执行了')
- }, 1000)
- })
- return <div>Foodiv>
- }
-
-
- function App() {
- const [flag, setFlag] = useState(true)
- return (
- <>
- <button onClick={() => setFlag(false)}>clickbutton>
- {flag ? <Foo/> : null}
- >
- )
- }
-
- export default App
添加清理副作用函数后:一旦组件被销毁,定时器也被清理
- import { useEffect, useState } from 'react'
-
- function Foo() {
- useEffect(() => {
- const timerId = setInterval(() => {
- console.log('副作用函数执行了')
- }, 1000)
- // 添加清理副租用函数
- return () => {
- clearInterval(timerId)
- }
- })
- return <div>Foodiv>
- }
- function App() {
- const [flag, setFlag] = useState(true)
- return (
- <>
- <button onClick={() => setFlag(false)}>clickbutton>
- {flag ? <Foo/> : null}
- >
- )
- }
-
- export default App
类组件如何发送网络请求?
发送网络请求的生命周期钩子函数:componentDidMount
执行时机?
在初始化的时候dom渲染完毕时,只执行一次
useEffect
如何在useEffect中发送网络请求,并且封装同步 async await操作
不可以直接在useEffect的回调函数外层直接包裹 await ,因为异步会导致清理函数无法立即返回
- useEffect(async ()=>{
- const res = await axios.get('http://geek.itheima.net/v1_0/channels')
- console.log(res)
- },[])
在内部单独定义一个函数,然后把这个函数包装成同步
- useEffect(()=>{
- async function fetchData(){
- // await fetch也可以,百度搜索
- const res = await axios.get('http://geek.itheima.net/v1_0/channels')
- console.log(res)
- }
- },[])
复制一张图片的地址,可以将图片请求出来:
- function A () {
- const [imgs, setImgs] = useState('')
- useEffect(() => {
- const res = fetch('https://fastly.jsdelivr.net/npm/@vant/assets/logo.png')
- console.log(res)
- res.then((value) => {
- console.log(value)//返回的是一个结果
- setImgs(value.url)
- })
- },[])
-
- return (
- <div>
- A组件
- <img src={imgs} alt="" />
- div>
- )}
useEffect发送网络请求,只请求一次的话,依赖项就设置为空数组 。
useRef获取真实dom或组件实例。返回一个可变的ref对象,其中.current属性被初始化为传入的参数,
在函数组件中获取真实的dom元素对象或者是组件对象
useRef
函数useRef
函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例)- import { useEffect, useRef } from 'react'
-
- class Test extends React.Component {
- render () {
- return (
- <div>类组件div>
- )
- }
- }
-
- function App() {
- const testRef = useRef(null)
- const h1Ref = useRef(null)
- //useEffect回调:在dom渲染之后
- // vue里的watch效果比较像,但执行时机时不同的
- useEffect(() => {
- console.log(testRef.current)
- console.log(h1Ref.current)
- console.log(h1Ref)
- },[])
- return (
- <div>
- <Test ref={testRef} />
- <h1 ref={ h1Ref }>this is h1h1>
- div>
- )
- }
- export default App
在Test中加入函数,state等
- state={
- name:'zz'
- }
- getName=()=>{
- return 'child test'
- }
函数组件由于没有实例,不能使用ref获取,如果想获取组件实例,必须是类组件
Foo.js
- class Foo extends React.Component {
- sayHi = () => {
- console.log('say hi')
- }
- render(){
- return <div>Foodiv>
- }
- }
-
- export default Foo
App.js
- import { useEffect, useRef } from 'react'
- import Foo from './Foo'
- function App() {
- const h1Foo = useRef(null)
- useEffect(() => {
- console.log(h1Foo)
- }, [])
- return (
- <div> <Foo ref={ h1Foo } />div>
- )
- }
- export default App
在函数组件中跨组件通信
React.createContext
的返回值)并返回该 context 的当前值。
的 value
prop 决定。 createContext
创建Context对象Provider
提供数据useContext
函数获取数据- import { createContext, useContext } from 'react'
- // 1.创建Context对象
- const Context = createContext()
-
- function Foo() {
- return <div>Foo <Bar/>div>
- }
-
- function Bar() {
- // 3.底层组件通过useContext函数获取数据
- // usseContext(createContext返回的对象)
- //通过调用useContext,将Context对象传过去,得到当前的传过去的Context的值
- const name = useContext(Context)
- return <div>Bar {name}div>
- }
-
- function App() {
- return (
- // 2.顶层组件通过Provider 提供数据
- <Context.Provider value={'this is name'}>
- <div><Foo/>div>
- Context.Provider>
- )
- }
-
- export default App
1.把createContext分装出去,创建context.js ---要记得都保存
- import { createContext } from "react"
-
- const Context = createContext()
-
- export default Context
2.App.js引用context.js
import Context from './context'
完整代码:
- import React, { useContext, useState } from 'react'
-
- import Context from './context'
-
- function ComA () {
- const count = useContext(Context)
- return (
- <div>
- ComA
- <br />
- app传过来的数据为:{count}
- <ComC />
- div>
- )
- }
-
- function ComC () {
- const count = useContext(Context)
- return (
- <div>
- ComC
- <br />
- app传过来的数据为:{count}
- div>
- )
- }
-
- function App () {
- const [count, setCount] = useState(20)
- return (
-
- <Context.Provider value={count}>
- <div>
- <ComA />
- <button onClick={() => { setCount(count + 1) }}>+button>
- div>
- Context.Provider>
- )
- }
-
- export default App
1.index.js中引入context.js
import Context from './context'
2.除去App.js中的以下内容
- <Context.Provider value={count}>
- const [count, setCount] = useState(20)
3.index.js中添加
- <React.StrictMode>
- // 这个value只能传递一次,是什么就是什么
- <Context.Provider value={100}>
- <App />
- Context.Provider>
- React.StrictMode>,
context如果要传递的数据,只需要在整个应用初始化的时候传递一次就可以,可以选择在当前文件里做数据提供,index.js->静态的
context如果需要传递数据并且将来还需要再对数据做修改,底层组件也需要变,所以可以写到App.js中->动态的
案例仓库地址:
项目演示步骤:
git clone https://gitee.com/react-course-series/react-tomvc-hook.git
yarn
- # 启动mock服务
- yarn mock-serve
yarn start
项目开发步骤:
切换到todo-test分支
git checkout todo-test
打开 app.js
已有基础样板代码,在这个基础上编写业务逻辑即可
接口文档
接口作用 | 接口地址 | 接口方法 | 接口参数 |
---|---|---|---|
获取列表 | http://localhost:3001/data | GET | 无 |
删除 | http://localhost:3001/data/:id | DELETE | id |
搜索 | http://localhost:3001/data/?q=keyword | GET | name(以name字段搜索) |
实现功能
功能 | 核心思路 |
---|---|
表格数据渲染 | elementPlus el-table组件使用 |
删除功能 | 获取当前id 调用接口 |
搜索功能 | 用的依旧是列表接口,多传一个name参数 |
清除搜索功能 | 清空搜索参数 重新获取列表 |