面试官:"React 事件绑定怎么写?"
你:"
onClick={this.handleClick}
。"面试官:"
this
指向呢?"你:"......"
别怕,今天把 React 事件绑定的坑全填掉!
一、React 事件绑定:3 种写法
1️⃣ 绑定方法(类组件)
jsx
class Demo extends React.Component {
handleClick = () => {
console.log('Clicked!');
};
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
- 箭头函数 :自动绑定
this
,最省心。
2️⃣ 绑定实例方法(类组件)
jsx
class Demo extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Clicked!');
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
- bind:在构造函数里绑定,适合普通函数。
3️⃣ 函数组件 + 事件处理
jsx
function Demo() {
const handleClick = () => {
console.log('Clicked!');
};
return <button onClick={handleClick}>Click me</button>;
}
- 函数组件:直接定义函数,简洁明了。
二、事件对象:e
是谁?
jsx
function handleChange(e) {
console.log(e.target.value); // 拿到输入框的值
}
function Demo() {
return <input onChange={handleChange} />;
}
e
是 合成事件对象,类似原生事件,但跨浏览器。
三、事件委托:少写 80% 的绑定代码
jsx
function Demo() {
const handleChildClick = (id) => {
console.log(`Child ${id} clicked`);
};
return (
<ul onClick={(e) => handleChildClick(e.target.dataset.id)}>
<li data-id="1">Item 1</li>
<li data-id="2">Item 2</li>
</ul>
);
}
- 利用
e.target
,在父级统一处理子级事件。
四、防抖节流:输入框的性能救星
防抖(Debounce)
jsx
function useDebounce(callback, delay) {
const timer = useRef(null);
const debouncedCallback = useCallback((...args) => {
clearTimeout(timer.current);
timer.current = setTimeout(() => callback(...args), delay);
}, [callback, delay]);
return debouncedCallback;
}
function Demo() {
const handleChange = useDebounce((e) => {
console.log('Typing:', e.target.value);
}, 500);
return <input onChange={handleChange} />;
}
节流(Throttle)
jsx
function useThrottle(callback, limit) {
const lastCall = useRef(Date.now());
const throttledCallback = useCallback((...args) => {
const now = Date.now();
if (now - lastCall.current >= limit) {
callback(...args);
lastCall.current = now;
}
}, [callback, limit]);
return throttledCallback;
}
function Demo() {
const handleChange = useThrottle((e) => {
console.log('Typing:', e.target.value);
}, 500);
return <input onChange={handleChange} />;
}
五、事件穿透:阻止冒泡,让父级"看不见"
jsx
function Demo() {
const handleClick = (e) => {
e.stopPropagation(); // 阻止冒泡
console.log('Inner clicked');
};
const handleOuterClick = () => {
console.log('Outer clicked');
};
return (
<div onClick={handleOuterClick}>
Outer
<div onClick={handleClick}>Inner</div>
</div>
);
}
六、自定义事件:给组件加"私有信号"
jsx
const EventEmitter = () => {
const listeners = useRef({});
const on = (eventName, callback) => {
listeners.current[eventName] = listeners.current[eventName] || [];
listeners.current[eventName].push(callback);
};
const emit = (eventName, ...args) => {
const eventListeners = listeners.current[eventName] || [];
eventListeners.forEach((listener) => listener(...args));
};
return { on, emit };
};
function Child() {
const { emit } = useContext(EventEmitterContext);
const handleCustomEvent = () => {
emit('customEvent', 'Hello from Child!');
};
return <button onClick={handleCustomEvent}>Trigger Custom Event</button>;
}
function Parent() {
const { on } = useContext(EventEmitterContext);
useEffect(() => {
on('customEvent', (message) => {
console.log('Custom event received:', message);
});
}, [on]);
return <Child />;
}
七、总结:React 事件绑定速记卡
场景 | 写法 |
---|---|
类组件绑定 | this.handleClick = this.handleClick.bind(this); 或箭头函数 |
函数组件 | const handleClick = () => { ... }; |
事件对象 | e.target.value 拿值 |
事件委托 | e.target.dataset.id 读属性 |
防抖 | useDebounce(callback, delay) |
节流 | useThrottle(callback, limit) |
阻止冒泡 | e.stopPropagation() |
自定义事件 | emit('eventName', data) + on('eventName', callback) |
🏁 一句话总结
React 事件绑定:类组件用箭头函数或 bind,函数组件直接定义;事件对象用 e.target
;防抖节流优化输入框;阻止冒泡控制事件范围;自定义事件跨组件通信。把这张表贴在工位,面试再也不怕问事件绑定!