如何在不在 React 应用程序中设置状态的情况下执行组件中的更改?
How to execute change in a component without setting state in a react app?
如果按下右箭头键或左箭头键,下面的组件将设置状态。因为每个按键执行两个设置状态,会导致一些性能问题。如何在不设置状态或仅设置一种状态的情况下更改按键?
import { useState, useEffect } from "react";
export function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState(false)
function downHandler({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}
const upHandler = ({ key }) => {
if (key === targetKey) {
setKeyPressed(false);
}
}
useEffect(() => {
window.addEventListener("keydown", downHandler);
window.addEventListener("keyup", upHandler);
return () => {
window.removeEventListener("keydown", downHandler);
window.removeEventListener("keyup", upHandler);
}
})
return keyPressed
}
如果您总是需要 keyPressed
的当前值,则无法绕过设置状态,但是您 可以 像这样通过 specifying the dependency list and calling useRef()
跳过效果:
export function useEventListener(targetRef, type, handler) {
const handlerRef = useRef(null);
useEffect(() => {
handlerRef.current = handler;
}, [handler]);
useEffect(() => {
const target = targetRef.current;
const listener = (event) => handlerRef.current(event);
target.addEventListener(type, listener);
return () => {
target.removeEventListener(type, listener);
};
}, [targetRef, type]);
}
然后你可以这样定义你的函数:
export function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState(false);
const windowRef = useRef(window);
useEventListener(windowRef, 'keydown', ({ key }) => {
if (key === targetKey) {
setKeyPressed(true);
}
});
useEventListener(windowRef, 'keyup', ({ key }) => {
if (key === targetKey) {
setKeyPressed(false);
}
});
return keyPressed;
}
虽然 targetRef
参数在上面的特定示例中乍一看似乎是迂回的,但它非常有用,例如当您想向 JSX 元素添加事件侦听器时,如下所示:
const { useEffect, useRef } = React;
function useEventListener(targetRef, type, handler) {
const handlerRef = useRef(null);
useEffect(() => {
handlerRef.current = handler;
}, [handler]);
useEffect(() => {
const target = targetRef.current;
const listener = (event) => handlerRef.current(event);
target.addEventListener(type, listener);
return () => {
target.removeEventListener(type, listener);
};
}, [targetRef, type]);
}
function App() {
const buttonRef = useRef(null);
useEventListener(buttonRef, 'click', () => {
console.log('button clicked');
});
return (
<button ref={buttonRef}>Click Me</button>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
如果按下右箭头键或左箭头键,下面的组件将设置状态。因为每个按键执行两个设置状态,会导致一些性能问题。如何在不设置状态或仅设置一种状态的情况下更改按键?
import { useState, useEffect } from "react";
export function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState(false)
function downHandler({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}
const upHandler = ({ key }) => {
if (key === targetKey) {
setKeyPressed(false);
}
}
useEffect(() => {
window.addEventListener("keydown", downHandler);
window.addEventListener("keyup", upHandler);
return () => {
window.removeEventListener("keydown", downHandler);
window.removeEventListener("keyup", upHandler);
}
})
return keyPressed
}
如果您总是需要 keyPressed
的当前值,则无法绕过设置状态,但是您 可以 像这样通过 specifying the dependency list and calling useRef()
跳过效果:
export function useEventListener(targetRef, type, handler) {
const handlerRef = useRef(null);
useEffect(() => {
handlerRef.current = handler;
}, [handler]);
useEffect(() => {
const target = targetRef.current;
const listener = (event) => handlerRef.current(event);
target.addEventListener(type, listener);
return () => {
target.removeEventListener(type, listener);
};
}, [targetRef, type]);
}
然后你可以这样定义你的函数:
export function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState(false);
const windowRef = useRef(window);
useEventListener(windowRef, 'keydown', ({ key }) => {
if (key === targetKey) {
setKeyPressed(true);
}
});
useEventListener(windowRef, 'keyup', ({ key }) => {
if (key === targetKey) {
setKeyPressed(false);
}
});
return keyPressed;
}
虽然 targetRef
参数在上面的特定示例中乍一看似乎是迂回的,但它非常有用,例如当您想向 JSX 元素添加事件侦听器时,如下所示:
const { useEffect, useRef } = React;
function useEventListener(targetRef, type, handler) {
const handlerRef = useRef(null);
useEffect(() => {
handlerRef.current = handler;
}, [handler]);
useEffect(() => {
const target = targetRef.current;
const listener = (event) => handlerRef.current(event);
target.addEventListener(type, listener);
return () => {
target.removeEventListener(type, listener);
};
}, [targetRef, type]);
}
function App() {
const buttonRef = useRef(null);
useEventListener(buttonRef, 'click', () => {
console.log('button clicked');
});
return (
<button ref={buttonRef}>Click Me</button>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>