在本文中,我们将深入探讨自定义Hooks的世界,探讨它们的重要性,并提供具体的示例,展示如何利用它们来简化和增强React开发。
React中的自定义Hook是一个利用一个或多个内置Hook(如useState、useEffect、useContext等)的JavaScript函数。
自定义Hook提供了一种在React组件之间提取和共享逻辑的方式,促进了更清晰的代码、更好的组织和更高的生产力。
在React中创建自定义Hook遵循一组特定的规则和语法。
-
自定义Hook应始终以单词"use"开头,以遵循React设定的约定。例如,用于处理表单输入的自定义Hook可以命名为"useFormInput"。
-
自定义Hook可以利用useState、useEffect、useContext和其他内置Hook。它们还可以包含任何必要的自定义逻辑,以封装和抽象复杂的功能。
-
自定义Hook的主要目标是可重用性。它应该被设计为在应用程序的多个组件中使用,允许您抽象和共享常见的逻辑。
-
自定义Hook不应包含任何JSX或导致组件渲染。它们的目的是封装逻辑和状态管理,而不是定义UI。
import { useRef, useCallback } from "react";
export const useFocus = () => {
const ref = useRef(null);
const focusElement = useCallback(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
return [ref, focusElement];
};
export default function App() {
const [inputRef, focusInput] = useFocus();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
</div>
);
}
在这个例子中,useFocus自定义Hook返回一个包含ref和函数的数组。ref用于存储对DOM元素的引用,focusElement函数用于在调用时将焦点放在该元素上。
import { useState, useEffect } from "react";
export const useDelay = (delayTime) => {
const [done, setDone] = useState(false);
useEffect(() => {
const delay = setTimeout(() => {
setDone(true);
}, delayTime);
return () => clearTimeout(delay);
}, [delayTime]);
return done;
};
export default function App() {
const isDone = useDelay(2000);
return (
<div>
{isDone ? (
<p>Welcome to JavaScript Centric!</p>
) : (
<p>Page is Loading ...</p>
)}
</div>
);
}
在这个例子中,useDelay Hook在App组件中使用,以引入1000毫秒的延迟。根据延迟是否完成,组件呈现不同的内容。
import { useState, useEffect } from "react";
export const useWindowSize = () => {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return windowSize;
};
export default function App() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window Width: {width}</p>
<p>Window Height: {height}</p>
</div>
);
}
在这个例子中,useWindowSize Hook在App组件中使用,以跟踪窗口的尺寸并在组件中显示它们。这个useWindowSize自定义Hook封装了跟踪窗口尺寸的逻辑,使其在不同的组件中可重用,并促进了更具响应性和适应性的用户界面。
import React, { useRef, useEffect } from "react";
export const useOnClickOutside = (ref, handler) => {
useEffect(() => {
const listener = (event) => {
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener("mousedown", listener);
return () => {
document.removeEventListener("mousedown", listener);
};
}, [ref, handler]);
};
export default function App() {
const ref = useRef(null);
const onClose = () => {
alert("clicked outside");
};
useOnClickOutside(ref, onClose);
return <div ref={ref}>Click outside this element to close</div>;
}
import React, { useState, useEffect } from "react";
export const useFormUpdate = () => {
const [isFormUpdated, setIsFormUpdated] = useState(false);
useEffect(() => {
const handleBeforeUnload = (event) => {
if (isFormUpdated) {
const message =
"You have unsaved changes. Are you sure you want to leave?";
event.returnValue = message;
return message;
}
};
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
};
}, [isFormUpdated]);
const handleFormUpdate = () => {
setIsFormUpdated(true);
};
useEffect(() => {
const formElements = document.querySelectorAll(
"form input, form select, form textarea"
);
const handleFieldChange = () => {
setIsFormUpdated(true);
};
formElements.forEach((element) => {
element.addEventListener("change", handleFieldChange);
});
return () => {
formElements.forEach((element) => {
element.removeEventListener("change", handleFieldChange);
});
};
}, []);
return handleFormUpdate;
};
export default function App() {
useFormUpdate();
return (
<form>
<input type="text" />
<input type="email" />
<textarea />
<select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<input type="submit" value="Submit" />
</form>
);
}
在这个例子中,useFormUpdate Hook在App组件中使用,自动监视所有表单字段的更改,包括输入、文本区域和选择元素。当用户修改任何表单字段时,表单被标记为已更新,并且当用户尝试导航到未保存的更改时,beforeunload事件监听器被触发。
import React, { useState, useEffect } from "react";
export const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
export default function App() {
const users = useFetch("https://jsonplaceholder.typicode.com/users");
return (
<div>
{users.loading ? (
<p>Loading...</p>
) : users.error ? (
<p>Error: {users.error.message}</p>
) : (
<ul>
{users.data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
);
}
import React, { useState, useEffect } from "react";
export const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
export default function App() {
const [inputValue, setInputValue] = useState("");
const debouncedValue = useDebounce(inputValue, 500);
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
return (
<div>
<input type="text" value={inputValue} onChange={handleInputChange} />
<p>Debounced value: {debouncedValue}</p>
</div>
);
}
在这个例子中,useDebounce Hook用于防抖动文本输入字段的输入值。在指定的延迟之后,debouncedValue会更新,使您能够更高效地处理输入更改,例如用于实时搜索功能。
import React, { useState, useEffect } from "react";
export const useLocalStorage = (key, initialValue) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
console.log("valueToStore", valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};```javascript
useEffect(() => {
const handleStorageChange = () => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.error(error);
}
};
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}, [key, initialValue]);
return [storedValue, setValue];
};
export default function App() {
const [token, setToken] = useLocalStorage("tokenName", "");
console.log("token", token);
const handleTokenChange = (event) => {
setToken(event.target.value);
};
return (
<div>
<input type="text" value={token} onChange={handleTokenChange} />
</div>
);
}
在这个例子中,useLocalStorage hook 在 App 组件中被使用来持久化和获取与 "tokenName" 键相关联的值。该组件渲染一个输入框,允许用户更新持久化的值。
import React, { useState, useEffect } from "react";
export const useMediaQuery = (query) => {
const [matches, setMatches] = useState(false);
useEffect(() => {
const mediaQuery = window.matchMedia(query);
const handleMediaQueryChange = (event) => {
setMatches(event.matches);
};
mediaQuery.addListener(handleMediaQueryChange);
setMatches(mediaQuery.matches);
return () => {
mediaQuery.removeListener(handleMediaQueryChange);
};
}, [query]);
return matches;
};
export default function App() {
const isMobile = useMediaQuery("(max-width: 768px)");
return <div>{isMobile ? <p>移动端视图</p> : <p>桌面端视图</p>}</div>;
}
在这个例子中,useMediaQuery hook 在 App 组件中被使用来检测基于指定媒体查询的视口大小的变化。该组件根据 useMediaQuery hook 返回的 isMobile 状态有条件地渲染内容。
import React, { useState } from "react";
export const useToggle = (defaultValue) => {
const [value, setValue] = useState(defaultValue);
function toggleValue(value) {
setValue((currentValue) =>
typeof value === "boolean" ? value : !currentValue
);
}
return [value, toggleValue];
};
export default function App() {
const [value, toggleValue] = useToggle(false);
return (
<div>
<div>{value.toString()}</div>
<button onClick={toggleValue}>切换</button>
<button onClick={() => toggleValue(true)}>设为真</button>
<button onClick={() => toggleValue(false)}>设为假</button>
</div>
);
}
在这个例子中,useToggle hook 在 App 组件中被使用来在 true 和 false 之间切换一个值。