1、Hook
1.1 為什么會(huì)出現(xiàn)hook
1.2 useState
import React, { memo, useState } from "react";
const App = memo(() => {
const [message, setMessage] = useState("Hello World");
const [count, setCount] = useState(100);
const [banners, setBanners] = useState([]);
function changeMessage() {
setMessage("你好啊, 李銀河!");
}
return (
<div>
<h2>App: {message}</h2>
<button onClick={changeMessage}>修改文本</button>
</div>
);
});
export default App;
1.3 useEffect
import React, { memo, useEffect } from 'react'
import { useState } from 'react'
const App = memo(() => {
const [count, setCount] = useState(0)
// 負(fù)責(zé)告知react, 在執(zhí)行完當(dāng)前組件渲染之后要執(zhí)行的副作用代碼
useEffect(() => {
// 1.監(jiān)聽(tīng)事件
// const unubscribe = store.subscribe(() => {
// })
// function foo() {
// }
// eventBus.on("why", foo)
console.log("監(jiān)聽(tīng)redux中數(shù)據(jù)變化, 監(jiān)聽(tīng)eventBus中的why事件")
// 返回值: 回調(diào)函數(shù) => 組件被重新渲染或者組件卸載的時(shí)候執(zhí)行
return () => {
console.log("取消監(jiān)聽(tīng)redux中數(shù)據(jù)變化, 取消監(jiān)聽(tīng)eventBus中的why事件")
}
})
return (
<div>
<button onClick={e => setCount(count+1)}>+1({count})</button>
</div>
)
})
export default App
import React, { memo, useEffect } from 'react'
import { useState } from 'react'
const App = memo(() => {
const [count, setCount] = useState(0)
// 負(fù)責(zé)告知react, 在執(zhí)行完當(dāng)前組件渲染之后要執(zhí)行的副作用代碼
useEffect(() => {
// 1.修改document的title(1行)
console.log("修改title")
})
// 一個(gè)函數(shù)式組件中, 可以存在多個(gè)useEffect
useEffect(() => {
// 2.對(duì)redux中數(shù)據(jù)變化監(jiān)聽(tīng)(10行)
console.log("監(jiān)聽(tīng)redux中的數(shù)據(jù)")
return () => {
// 取消redux中數(shù)據(jù)的監(jiān)聽(tīng)
}
})
useEffect(() => {
// 3.監(jiān)聽(tīng)eventBus中的why事件(15行)
console.log("監(jiān)聽(tīng)eventBus的why事件")
return () => {
// 取消eventBus中的why事件監(jiān)聽(tīng)
}
})
return (
<div>
<button onClick={e => setCount(count+1)}>+1({count})</button>
</div>
)
})
export default App
import React, { memo, useEffect } from "react";
import { useState } from "react";
const App = memo(() => {
const [count, setCount] = useState(0);
const [message, setMessage] = useState("Hello World");
// 只收到count的影響
useEffect(() => {
console.log("修改title:", count);
}, [count]);
// 不受任何影響 類似生命周期的掛載和卸載
useEffect(() => {
console.log("發(fā)送網(wǎng)絡(luò)請(qǐng)求, 從服務(wù)器獲取數(shù)據(jù)");
return () => {
console.log("會(huì)在組件被卸載時(shí), 才會(huì)執(zhí)行一次");
};
}, []);
return (
<div>
<button onClick={(e) => setCount(count + 1)}>+1({count})</button>
<button onClick={(e) => setMessage("你好啊")}>
修改message({message})
</button>
</div>
);
});
export default App;
1.4 useContext
import { createContext } from "react";
const UserContext = createContext();
const ThemeContext = createContext();
export { UserContext, ThemeContext };
import React, { memo, useContext } from 'react'
import { UserContext, ThemeContext } from "./context"
const App = memo(() => {
// 使用Context
const user = useContext(UserContext)
const theme = useContext(ThemeContext)
return (
<div>
<h2>User: {user.name}-{user.level}</h2>
<h2 style={{color: theme.color, fontSize: theme.size}}>Theme</h2>
</div>
)
})
export default App
- 依然需要在index.js使用context
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<UserContext.Provider value={{name: "why", level: 99}}>
<TokenContext.Provider value={'coderwhy'}>
<App />
</TokenContext.Provider>
</UserContext.Provider>
);
1.5 useReducer
import React, { memo, useReducer } from 'react'
// import { useState } from 'react'
function reducer(state, action) {
switch(action.type) {
case "increment":
return { ...state, counter: state.counter + 1 }
case "decrement":
return { ...state, counter: state.counter - 1 }
case "add_number":
return { ...state, counter: state.counter + action.num }
case "sub_number":
return { ...state, counter: state.counter - action.num }
default:
return state
}
}
// useReducer+Context => redux
const App = memo(() => {
// const [count, setCount] = useState(0)
const [state, dispatch] = useReducer(reducer, { counter: 0, friends: [], user: {} })
// const [counter, setCounter] = useState()
// const [friends, setFriends] = useState()
// const [user, setUser] = useState()
return (
<div>
{/* <h2>當(dāng)前計(jì)數(shù): {count}</h2>
<button onClick={e => setCount(count+1)}>+1</button>
<button onClick={e => setCount(count-1)}>-1</button>
<button onClick={e => setCount(count+5)}>+5</button>
<button onClick={e => setCount(count-5)}>-5</button>
<button onClick={e => setCount(count+100)}>+100</button> */}
<h2>當(dāng)前計(jì)數(shù): {state.counter}</h2>
<button onClick={e => dispatch({type: "increment"})}>+1</button>
<button onClick={e => dispatch({type: "decrement"})}>-1</button>
<button onClick={e => dispatch({type: "add_number", num: 5})}>+5</button>
<button onClick={e => dispatch({type: "sub_number", num: 5})}>-5</button>
<button onClick={e => dispatch({type: "add_number", num: 100})}>+100</button>
</div>
)
})
export default App
1.6 useCallback
import React, { memo, useState, useCallback, useRef } from "react";
// useCallback性能優(yōu)化的點(diǎn):
// 1.當(dāng)需要將一個(gè)函數(shù)傳遞給子組件時(shí), 最好使用useCallback進(jìn)行優(yōu)化, 將優(yōu)化之后的函數(shù), 傳遞給子組件
// props中的屬性發(fā)生改變時(shí), 組件本身就會(huì)被重新渲染
const HYHome = memo(function (props) {
const { increment } = props;
console.log("HYHome被渲染");
return (
<div>
<button onClick={increment}>increment+1</button>
{/* 100個(gè)子組件 */}
</div>
);
});
const App = memo(function () {
const [count, setCount] = useState(0);
const [message, setMessage] = useState("hello");
console.log("App組件被重新渲染");
// 閉包陷阱: useCallback
// 閉包陷阱的結(jié)果可以參考如下案例 bar2()始終拿到的是why
// 參數(shù)1 函數(shù)
// 參數(shù)2 依賴值 只有依賴值發(fā)生變化 函數(shù)才會(huì)更新
// const increment = useCallback(
// function foo() {
// console.log("increment");
// setCount(count + 1);
// },
// [count]
// );
// 進(jìn)一步的優(yōu)化: 當(dāng)count發(fā)生改變時(shí), 也使用同一個(gè)函數(shù)(了解)
// 做法一: 將count依賴移除掉, 缺點(diǎn): 閉包陷阱
// 做法二: useRef, 在組件多次渲染時(shí), 返回的是同一個(gè)值
const countRef = useRef();
countRef.current = count;
const increment = useCallback(function foo() {
console.log("increment");
setCount(countRef.current + 1);
}, []);
// 普通的函數(shù)
// 如果使用過(guò)方法的話 每次傳入組件的props的數(shù)值都是新的會(huì)導(dǎo)致組件被重新渲染
// const increment = () => {
// setCount(count + 1);
// };
return (
<div>
<h2>計(jì)數(shù): {count}</h2>
<button onClick={increment}>+1</button>
<HYHome increment={increment} />
<h2>message:{message}</h2>
<button onClick={(e) => setMessage(Math.random())}>修改message</button>
</div>
);
});
// function foo(name) {
// function bar() {
// console.log(name)
// }
// return bar
// }
// const bar1 = foo("why")
// bar1() // why
// bar1() // why
// const bar2 = foo("kobe")
// bar2() // kobe
// bar1() // why
export default App;
1.7 useMemo
import React, { memo, useCallback } from "react";
import { useMemo, useState } from "react";
const HelloWorld = memo(function (props) {
console.log("HelloWorld被渲染~");
return <h2>Hello World</h2>;
});
function calcNumTotal(num) {
// console.log("calcNumTotal的計(jì)算過(guò)程被調(diào)用~")
let total = 0;
for (let i = 1; i <= num; i++) {
total += i;
}
return total;
}
const App = memo(() => {
const [count, setCount] = useState(0);
// const result = calcNumTotal(50)
// 1.不依賴任何的值, 進(jìn)行計(jì)算
// 傳入的是數(shù)值 只要數(shù)值沒(méi)有發(fā)生變化 子組件就不會(huì)重新渲染
const result = useMemo(() => {
return calcNumTotal(50);
}, []);
// 2.依賴count
// const result = useMemo(() => {
// return calcNumTotal(count*2)
// }, [count])
// 3.useMemo和useCallback的對(duì)比
// useMemo是對(duì)值做優(yōu)化 而useCallBack是對(duì)函數(shù)做優(yōu)化
function fn() {}
// const increment = useCallback(fn, [])
// const increment2 = useMemo(() => fn, [])
// 4.使用useMemo對(duì)子組件渲染進(jìn)行優(yōu)化
// const info = { name: "why", age: 18 }
// 傳入的是對(duì)象的話 APP組件重新渲染對(duì)象也會(huì)被重新創(chuàng)建 導(dǎo)致子組件也重新渲染
const info = useMemo(() => ({ name: "why", age: 18 }), []);
return (
<div>
<h2>計(jì)算結(jié)果: {result}</h2>
<h2>計(jì)數(shù)器: {count}</h2>
<button onClick={(e) => setCount(count + 1)}>+1</button>
<HelloWorld result={result} info={info} />
</div>
);
});
export default App;
1.8 useRef
1.8.1 ref綁定dom
import React, { memo, useRef } from 'react'
const App = memo(() => {
const titleRef = useRef()
const inputRef = useRef()
function showTitleDom() {
console.log(titleRef.current)
inputRef.current.focus()
}
return (
<div>
<h2 ref={titleRef}>Hello World</h2>
<input type="text" ref={inputRef} />
<button onClick={showTitleDom}>查看title的dom</button>
</div>
)
})
export default App
1.8.2 ref解決閉包缺陷
import React, { memo, useRef } from "react";
import { useCallback } from "react";
import { useState } from "react";
let obj = null;
const App = memo(() => {
const [count, setCount] = useState(0);
const nameRef = useRef();
console.log(obj === nameRef);
obj = nameRef;
// 通過(guò)useRef解決閉包陷阱
// 這里的ref使用是保持一致的 不會(huì)發(fā)生改變 所以將可以將count賦值給ref
const countRef = useRef();
countRef.current = count;
// 這里閉包產(chǎn)生的原因是是 第一次就創(chuàng)建了一個(gè)函數(shù) count 執(zhí)行之后 = 1
// 而在第二次執(zhí)行的時(shí)候 依然是拿到count = 1的結(jié)果 并沒(méi)有發(fā)生改變
// 為了解決這個(gè)問(wèn)題就使用ref保存的數(shù)值
const increment = useCallback(() => {
setCount(countRef.current + 1);
}, []);
return (
<div>
<h2>Hello World: {count}</h2>
<button onClick={(e) => setCount(count + 1)}>+1</button>
<button onClick={increment}>+1</button>
</div>
);
});
export default App;
1.9 useImperativeHandle
import React, { memo, useRef, forwardRef, useImperativeHandle } from "react";
const HelloWorld = memo(
forwardRef((props, ref) => {
const inputRef = useRef();
// 子組件對(duì)父組件傳入的ref進(jìn)行處理
// 不讓父組件直接拿到input的所有方法 所以將自己想要拋出去的方法包裹起來(lái)
useImperativeHandle(ref, () => {
return {
focus() {
console.log("focus");
inputRef.current.focus();
},
setValue(value) {
inputRef.current.value = value;
},
};
});
return <input type="text" ref={inputRef} />;
})
);
const App = memo(() => {
const titleRef = useRef();
const inputRef = useRef();
function handleDOM() {
// console.log(inputRef.current)
inputRef.current.focus();
// 不能直接通過(guò).value的方法來(lái)修改 只能通過(guò)setValue的方法來(lái)修改
// inputRef.current.value = ""
inputRef.current.setValue("哈哈哈");
}
return (
<div>
<h2 ref={titleRef}>哈哈哈</h2>
<HelloWorld ref={inputRef} />
<button onClick={handleDOM}>DOM操作</button>
</div>
);
});
export default App;
1.10 useLayoutEffect
import React, { memo, useEffect, useLayoutEffect, useState } from "react";
const App = memo(() => {
const [count, setCount] = useState(100);
// 這時(shí)候已經(jīng)顯示到界面上 才執(zhí)行修改count的數(shù)值會(huì)導(dǎo)致頁(yè)面閃爍
// useEffect(() => {
// console.log("useEffect");
// if (count === 0) {
// setCount(Math.random() + 99);
// }
// });
useLayoutEffect(() => {
console.log("useLayoutEffect");
if (count === 0) {
setCount(Math.random() + 99);
}
});
console.log("App render");
return (
<div>
<h2>count: {count}</h2>
<button onClick={(e) => setCount(0)}>設(shè)置為0</button>
</div>
);
});
export default App;
1.11 自定義Hook
1.11.1 什么是自定義Hook
import React, { memo, useEffect, useState } from 'react'
function useLogLife(cName) {
useEffect(() => {
console.log(cName + "組件被創(chuàng)建")
return () => {
console.log(cName + "組件被銷毀")
}
}, [cName])
}
const Home = memo(() => {
useLogLife("home")
return <h1>Home Page</h1>
})
const About = memo(() => {
useLogLife("about")
return <h1>About Page</h1>
})
const App = memo(() => {
const [isShow, setIsShow] = useState(true)
useLogLife("app")
return (
<div>
<h1>App Root Component</h1>
<button onClick={e => setIsShow(!isShow)}>切換</button>
{ isShow && <Home/> }
{ isShow && <About/> }
</div>
)
})
export default App
1.11.2 Context的共享
import { createContext } from "react";
const UserContext = createContext();
const TokenContext = createContext();
export { UserContext, TokenContext };
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<UserContext.Provider value={{ name: "why", level: 99 }}>
<TokenContext.Provider value={"coderwhy"}>
<App />
</TokenContext.Provider>
</UserContext.Provider>
);
Hook
import { useContext } from "react";
import { UserContext, TokenContext } from "../context";
function useUserToken() {
const user = useContext(UserContext);
const token = useContext(TokenContext);
return [user, token];
}
export default useUserToken;
import React, { memo } from 'react'
import { useUserToken } from "./hooks"
// User/Token
const Home = memo(() => {
const [user, token] = useUserToken()
return <h1>Home Page: {user.name}-{token}</h1>
})
const About = memo(() => {
const [user, token] = useUserToken()
return <h1>About Page: {user.name}-{token}</h1>
})
const App = memo(() => {
return (
<div>
<h1>App Root Component</h1>
<Home/>
<About/>
</div>
)
})
export default App
1.11.3 獲取鼠標(biāo)滾動(dòng)位置
import React, { memo } from 'react'
import useScrollPosition from './hooks/useScrollPosition'
import "./style.css"
const Home = memo(() => {
const [scrollX, scrollY] = useScrollPosition()
return <h1>Home Page: {scrollX}-{scrollY}</h1>
})
const About = memo(() => {
const [scrollX, scrollY] = useScrollPosition()
return <h1>About Page: {scrollX}-{scrollY}</h1>
})
const App = memo(() => {
return (
<div className='app'>
<h1>App Root Component</h1>
<Home/>
<About/>
</div>
)
})
export default App
import { useState, useEffect } from "react";
function useScrollPosition() {
const [scrollX, setScrollX] = useState(0);
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
function handleScroll() {
// console.log(window.scrollX, window.scrollY)
// 監(jiān)聽(tīng)到滾動(dòng)就把值保存下來(lái)
setScrollX(window.scrollX);
setScrollY(window.scrollY);
}
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return [scrollX, scrollY];
}
export default useScrollPosition;
1.11.4 storage
import { useEffect } from "react";
import { useState } from "react";
function useLocalStorage(key) {
// 1.從localStorage中獲取數(shù)據(jù), 并且數(shù)據(jù)數(shù)據(jù)創(chuàng)建組件的state
const [data, setData] = useState(() => {
const item = localStorage.getItem(key);
if (!item) return "";
return JSON.parse(item);
});
// 2.監(jiān)聽(tīng)data改變, 一旦發(fā)生改變就存儲(chǔ)data最新值
useEffect(() => {
localStorage.setItem(key, JSON.stringify(data));
}, [data]);
// 3.將data/setData的操作返回給組件, 讓組件可以使用和修改值
return [data, setData];
}
export default useLocalStorage;
import React, { memo } from 'react'
import { useEffect } from 'react'
import { useState } from 'react'
import useLocalStorage from './hooks/useLocalStorage'
const App = memo(() => {
// 通過(guò)key, 直接從localStorage中獲取一個(gè)數(shù)據(jù)
// const [token, setToken] = useState(localStorage.getItem("token"))
// useEffect(() => {
// localStorage.setItem("token", token)
// }, [token])
const [token, setToken] = useLocalStorage("token")
function setTokenHandle() {
setToken("james")
}
// const [avatarUrl, setAvatarUrl] = useState(localStorage.getItem("avatarUrl"))
// useEffect(() => {
// localStorage.setItem("avatarUrl", avatarUrl)
// }, [avatarUrl])
const [avatarUrl, setAvatarUrl] = useLocalStorage("avatarUrl")
function setAvatarUrlHandle() {
setAvatarUrl("http://www.james.com/cba.png")
}
return (
<div className='app'>
<h1>App Root Component: {token}</h1>
<button onClick={setTokenHandle}>設(shè)置token</button>
<h1>avatarURL: {avatarUrl}</h1>
<button onClick={setAvatarUrlHandle}>設(shè)置新頭像地址</button>
</div>
)
})
export default App
1.12 redux中的hook
import React, { memo } from 'react'
import { connect } from "react-redux"
import { addNumberAction, subNumberAction } from './store/modules/counter'
const App = memo((props) => {
const { count, addNumber, subNumber } = props
function addNumberHandle(num, isAdd = true) {
if (isAdd) {
addNumber(num)
} else {
subNumber(num)
}
}
return (
<div>
<h2>當(dāng)前計(jì)數(shù): {count}</h2>
<button onClick={e => addNumberHandle(1)}>+1</button>
<button onClick={e => addNumberHandle(6)}>+6</button>
<button onClick={e => addNumberHandle(6, false)}>-6</button>
</div>
)
})
const mapStateToProps = (state) => ({
count: state.counter.count
})
const mapDispatchToProps = (dispatch) => ({
addNumber(num) {
dispatch(addNumberAction(num))
},
subNumber(num) {
dispatch(subNumberAction(num))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(App)
import React, { memo } from "react";
import { useSelector, useDispatch, shallowEqual } from "react-redux";
import {
addNumberAction,
changeMessageAction,
subNumberAction,
} from "./store/modules/counter";
// memo高階組件包裹起來(lái)的組件有對(duì)應(yīng)的特點(diǎn): 只有props發(fā)生改變時(shí), 才會(huì)重新渲染
// 而且useSelector監(jiān)聽(tīng)的是state的變化 所以state變化的時(shí)候也會(huì)引起 重新渲染
// 加上shallowEqual 就只會(huì)在我們使用的參數(shù)發(fā)生變化的時(shí)候才重新渲染
const Home = memo((props) => {
const { message } = useSelector(
(state) => ({
message: state.counter.message,
}),
shallowEqual
);
// 使用dispatch 可以直接使用reducer
const dispatch = useDispatch();
function changeMessageHandle() {
dispatch(changeMessageAction("你好啊, 師姐!"));
}
console.log("Home render");
return (
<div>
<h2>Home: {message}</h2>
<button onClick={(e) => changeMessageHandle()}>修改message</button>
</div>
);
});
const App = memo((props) => {
// 1.使用useSelector將redux中store的數(shù)據(jù)映射到組件內(nèi)
const { count } = useSelector(
(state) => ({
count: state.counter.count,
}),
shallowEqual
);
// 2.使用dispatch直接派發(fā)action
const dispatch = useDispatch();
function addNumberHandle(num, isAdd = true) {
if (isAdd) {
dispatch(addNumberAction(num));
} else {
dispatch(subNumberAction(num));
}
}
console.log("App render");
return (
<div>
<h2>當(dāng)前計(jì)數(shù): {count}</h2>
<button onClick={(e) => addNumberHandle(1)}>+1</button>
<button onClick={(e) => addNumberHandle(6)}>+6</button>
<button onClick={(e) => addNumberHandle(6, false)}>-6</button>
<Home />
</div>
);
});
export default App;
1.13 講講SPA和 Hydration
SPA頁(yè)面
-
1、不利于SEO優(yōu)化搜索引擎優(yōu)化
-
2、首屏渲染速度慢
-
1、不利于SEO優(yōu)化搜索引擎優(yōu)化
-
- 例如百度頁(yè)面在進(jìn)行關(guān)鍵詞收錄的時(shí)候 是通過(guò)請(qǐng)求index.html頁(yè)面而SPA應(yīng)用的index.html頁(yè)面是很簡(jiǎn)單的,其他資源存在bundle中,另外還有一種就是獲取meta配置 meta配置也是有限的,會(huì)導(dǎo)致獲取到關(guān)于網(wǎng)站的資源比較少而導(dǎo)致網(wǎng)站排名靠后
-
2、首屏渲染速度慢
-
- 網(wǎng)站的資源都是通過(guò)
src = 'bundle.js'
去獲取 需要下載bundle.js文件 然后瀏覽器在執(zhí)行bundle.js 再通過(guò)document.createElement
講dom渲染到頁(yè)面上 如果請(qǐng)求的bundle.js文件過(guò)大 就會(huì)出現(xiàn)首屏渲染慢的問(wèn)題
- 網(wǎng)站的資源都是通過(guò)
1.14 useId
import React, { memo, useId, useState } from 'react'
const App = memo(() => {
const [count, setCount] = useState(0)
const id = useId()
console.log(id)
return (
<div>
<button onClick={e => setCount(count+1)}>count+1:{count}</button>
<label htmlFor={id}>
用戶名:<input id={id} type="text" />
</label>
</div>
)
})
export default App
1.15 useTransition
faker 這個(gè)庫(kù)可以模擬數(shù)據(jù)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-830285.html
import { faker } from '@faker-js/faker';
const namesArray = []
for (let i = 0; i < 10000; i++) {
namesArray.push(faker.name.fullName())
}
export default namesArray
import React, { memo, useState, useTransition } from "react";
import namesArray from "./namesArray";
const App = memo(() => {
const [showNames, setShowNames] = useState(namesArray);
// pending startTransition 函數(shù)執(zhí)行的狀態(tài)
const [pending, startTransition] = useTransition();
function valueChangeHandle(event) {
// 是在其他執(zhí)行完成之后 在執(zhí)行這里的函數(shù)
startTransition(() => {
const keyword = event.target.value;
const filterShowNames = namesArray.filter((item) =>
item.includes(keyword)
);
setShowNames(filterShowNames);
});
}
return (
<div>
<input type="text" onInput={valueChangeHandle} />
<h2>用戶名列表: {pending && <span>data loading</span>} </h2>
<ul>
{showNames.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
</div>
);
});
export default App;
1.16 useDeferredValue
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-830285.html
import React, { memo, useState, useDeferredValue } from "react";
import namesArray from "./namesArray";
const App = memo(() => {
const [showNames, setShowNames] = useState(namesArray);
// 跟useTransition的作用差不多 是對(duì)數(shù)據(jù)做處理而transition是對(duì)函數(shù)做處理
const deferedShowNames = useDeferredValue(showNames);
function valueChangeHandle(event) {
const keyword = event.target.value;
const filterShowNames = namesArray.filter((item) => item.includes(keyword));
setShowNames(filterShowNames);
}
return (
<div>
<input type="text" onInput={valueChangeHandle} />
<h2>用戶名列表: </h2>
<ul>
{deferedShowNames.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
</div>
);
});
export default App;
到了這里,關(guān)于react 【七】各種hooks的使用/SPA的缺點(diǎn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!