react18之自定义hook ()
- hook的使用规则
- 自定义hook本质而言就是一个函数,也就是抽离公共部门的代码,类似抽离组件或者说mixin(vue中的mixin)。
- hook必须以use开头(不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则)
- 内部正常使用useState useEffect或者其他hooks
- 自定义返回结果,格式不限
- 在两个组件中使用相同的 Hook 不会共享 state(自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。)
- 每次调用 Hook,它都会获取独立的 state(从 React 的角度来看,我们的组件只是调用了 useState 和 useEffect,一个组件中可以调用多次useState和useEffect,它们都是完全独立的)
01:自定义一个 简单的axios hook 发起get请求
useHttp.jsx
import {
useState, useEffect } from "react";
import axios from "axios";
export default function UseHttp(props) {
const {
url } = props;
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
axios.get(url)
.then((res) => {
setData(res);
})
.catch((err) => {
setError(err);
})
.finally(() => {
setLoading(false);
});
}, [url]);
return [loading, data, error];
}
使用useHttp hook
import React, {
useState,useEffect } from 'react';
import UseHttp from './useHttp';
export default function Base(props) {
const [list,setList] = useState([])
const url = 'http://localhost:9999' + '/list'
const [loading,data,error] = UseHttp({
url})
useEffect(()=>{
if ( data && data.status === 200) {
setList(data.data)
}
},[data])
return (
<div className='content'>
{
loading ? '加载中' :
<>
{
error ? <div>error</div> :
<div>
{
list.map(item=>{
return (
<div key={
item.id}>
name-{
item.name};
age- {
item.age }
</div>
)
})
}
</div>}
</>
}
</div>
)
}
效果
02:自定义一个 修改浏览器title hook
import {
useEffect } from 'react';
export default function useTitle(props) {
const {
title} = props
useEffect(() => {
document.title = title
return () => {
document.title = 'Original Title';
};
}, [title])
return {
title }
}
03:自定义一个 localStorage(获取、存储、移除) hook
useLocalStorage.jsx
import {
useState, useEffect } from 'react';
const useLocalStorage = (key, initialValue) => {
const [data, setData] = useState(() => {
let storageVal = localStorage.getItem(key);
return (storageVal && storageVal !== 'undefined') ? JSON.parse(storageVal) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(data));
}, [key, data]);
const removeLocalStorage = () => {
setData(initialValue);
localStorage.removeItem(key);
};
return [data, setData, removeLocalStorage];
};
export default useLocalStorage;
使用hook
import React, {
useState, useEffect } from 'react';
import useLocalStorage from './useLocalStorage';
export default function Base(props) {
const [name,setName,removeLocalStorage]= useLocalStorage('name','')
function getName(){
console.log('name',name);
}
function setLocalName(){
setName('我是setName')
}
function delLocalName(){
removeLocalStorage('name')
}
return (
<div className='content'>
<div>
<div>name-{
name}</div>
<div>获取name数据 - <button onClick={
getName}>getName</button></div>
<div>设置name数据 - <button onClick={
setLocalName}>setName</button></div>
<div>移除name数据 - <button onClick={
delLocalName}>delName</button></div>
</div>
</div>
)
}
效果
04:自定义一个 useScrollPosition(获取当前滚动条的位置) hook
useScrollPosition.jsx
import {
useState, useEffect } from 'react';
function useScrollPosition() {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const handleScroll = () => {
let scrollY = window.scrollY ? Math.round(window.scrollY) : 0
setScrollPosition(scrollY);
}
document.addEventListener("scroll", handleScroll);
return () => {
document.removeEventListener("scroll", handleScroll)
}
}, []);
return scrollPosition;
}
export default useScrollPosition;
使用
const scrollPosition = useScrollPosition()
console.log('scrollPosition', scrollPosition);
效果
05:自定义一个 useImageToBase64(img转换为base64) hook
useImageToBase64.jsx
import React, {
useState } from 'react';
const useImageToBase64 = () => {
const [base64Image, setBase64Image] = useState(null);
const imageToBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsDataURL(file);
});
};
const handleImageUpload = (event) => {
const file = event.target.files[0];
if (file) {
imageToBase64(file)
.then((base64) => {
setBase64Image(base64);
})
.catch((error) => {
console.error('Error converting image to Base64:', error);
});
}
};
return {
base64Image, handleImageUpload };
};
export default useImageToBase64;
使用
import React from 'react';
import useImageToBase64 from "../自定义hook/useImageToBase64 "
export default function Test(props) {
const {
base64Image, handleImageUpload } = useImageToBase64();
console.log('base64Image',base64Image);
return (
<div className='content' style={
{
marginTop:'40px'}}>
Test
<div>
<input type="file" accept="image/*" onChange={
handleImageUpload} />
{
base64Image && <img src={
base64Image} alt="Uploaded" />}
</div>
</div>
)
}
效果
06:自定义一个处理金额,保留位数的hook
hook.js
import {
useEffect, useState } from 'react';
const useFormattedDecimal = (val, decimal) => {
const [formattedDecimal, setFormattedDecimal] = useState('');
useEffect(() => {
const formatDecimal = (value, decimalPlaces) => {
try {
const decimalVal = decimalPlaces || 0;
const str = `${
Number(value).toFixed(decimalVal)}`;
if (decimalVal === 0) {
return str.replace(/\B(?=(?:\d{3})+$)/g, ',');
}
const intSum = str.substring(0, str.indexOf('.')).replace(/\B(?=(?:\d{3})+$)/g, ',');
const dot = str.substring(str.length, str.indexOf('.'));
return intSum + dot;
} catch (e) {
return 0;
}
};
setFormattedDecimal(formatDecimal(val, decimal));
}, [val, decimal]);
return formattedDecimal;
};
export {
useFormattedDecimal
};
使用hook
import React from 'react';
import {
useFormattedDecimal } from "./hook";
export default function App(props) {
return (
<div className='content'>
count - {
useFormattedDecimal(111.199,2) } <br/>
count2 - {
useFormattedDecimal(12345.2344,3) } <br/>
count3 - {
useFormattedDecimal(999333.35,2) } <br/>
count4 - {
useFormattedDecimal(18112833.652,2) }
</div>
);
}
效果
07:挂载、卸载、更新effect hook封装
hook.js
import {
useEffect, useRef, useState } from "react";
const useMount = (callback) => {
useEffect(callback, []);
};
const useUnMount = (callback) => {
useEffect(() => {
return () => callback();
}, []);
};
const useUpdateEffect = (effect, deps) => {
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
} else {
return effect();
}
},deps);
};
export {
useMount, useUnMount, useUpdateEffect };
使用 app.jsx
import React, {
useState } from 'react';
import Son from "./Son";
export default function App(props) {
const [sonShow,setSonShow] = useState(true)
const sonShowClick = ()=>{
setSonShow(!sonShow)
}
return (
<div className='content'>
{
sonShow ? <Son></Son> : 'son不展示'
}
<button onClick={
sonShowClick}>son展示</button>
</div>
);
}
Son.jsx
import React, {
useState } from 'react';
import {
useMount, useUnMount, useUpdateEffect } from "./hook";
export default function Son(props) {
const [count,setCount] = useState(0)
const [name,setName] = useState('name')
useMount(()=>{
console.log('useMount组件挂载');
})
useUnMount(()=>{
console.log('组件卸载时候-count',count);
})
useUpdateEffect(()=>{
console.log('useUpdateEffect-count',count);
},[count,name])
const addcount = ()=>{
setCount(count+ 1)
}
const changeName = () => {
setName('XXX')
}
return (
<div className='content'>
count - {
count } <br/>
<button onClick={
addcount}>addcount</button><br/>
name - {
name}<br/>
<button onClick={
changeName}>changeName</button>
</div>
);
}
08 useInput数据双向绑定hook
hook.js
const useInput = (initVal = '') => {
const [value,setValue] = useState(initVal)
const inputChange = (event) => {
setValue(event.target.value)
}
return {
value,
onChange: inputChange
}
}
export {
useInput };
使用
import React, {
useEffect } from 'react';
import {
useInput } from './hook';
export default function App(props) {
const nameValue = useInput('')
useEffect(()=>{
console.log('nameValue',nameValue.value);
},[nameValue.value])
return (
<div className='content'>
<input type="text" value={
nameValue.value} onChange={
nameValue.onChange}/>
nameValue -{
nameValue.value}
</div>
);
}
09 useToggle切换flag hook
hook.js
const useToggle = (initFlag = false) =>{
const [flag,setFlag] = useState(initFlag)
const toggle = () => {
setFlag((preVal)=> !preVal)
}
return [
flag,
toggle
]
}
export {
useToggle };
使用useToggle
import React from 'react';
import {
useToggle } from "./hook";
export default function App(props) {
const [flag,setFlag] = useToggle(false)
return (
<div className='content'>
<button onClick={
setFlag }>fffff</button>
{
flag ? '1' : '0'
}
</div>
);
}
000使用第三方hook库
npm install react-use
- 链接: 添加链接描述
简单的使用全屏
import React, {
useRef } from 'react';
import {
useFullscreen, useToggle } from 'react-use';
export default function App(props) {
const dom = useRef(null)
const [show, toggle] = useToggle(false);
useFullscreen(dom, show, {
onClose: () => toggle(false) });
return (
<div className='content'>
{
show ? <div ref={
dom} style={
{
color: "red",background: "#fff" }}>
我是可以全屏的div
<button onClick={
() => toggle()}>退出全屏</button>
</div> : ''
}
<button onClick={
() => toggle()}>全屏</button>
</div>
);
}