MST
星途 面试题库

面试题:React中useEventListener处理复杂事件场景

假设你有一个动态生成的列表,每个列表项都有一个点击事件,要求使用useEventListener来处理这些点击事件。但同时,当用户在列表外点击时,需要取消列表项的某些激活状态。请说明实现思路,并写出关键代码片段,阐述如何管理事件的绑定和解绑以避免内存泄漏。
39.1万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 处理列表项点击事件:使用 useEventListener 监听每个列表项的点击事件,在点击时更新该项的激活状态。
  2. 处理列表外点击事件:通过监听 document 的点击事件,判断点击目标是否在列表之外,如果是,则取消所有列表项的激活状态。
  3. 管理事件绑定和解绑:在组件挂载时绑定事件,在组件卸载时解绑事件,以避免内存泄漏。

关键代码片段

假设使用 React 框架,示例代码如下:

import React, { useState, useEffect } from'react';

// 自定义 useEventListener Hook
const useEventListener = (eventName, handler, element = document) => {
    useEffect(() => {
        const targetElement = element instanceof Window? window : element;
        targetElement.addEventListener(eventName, handler);
        return () => {
            targetElement.removeEventListener(eventName, handler);
        };
    }, [eventName, handler, element]);
};

const DynamicList = () => {
    const [listItems, setListItems] = useState([
        { id: 1, text: 'Item 1', isActive: false },
        { id: 2, text: 'Item 2', isActive: false },
        { id: 3, text: 'Item 3', isActive: false }
    ]);

    const handleListItemClick = (itemId) => {
        setListItems(prevItems => prevItems.map(item =>
            item.id === itemId? {...item, isActive:!item.isActive } : item
        ));
    };

    const handleOutsideClick = (event) => {
        const listElement = document.getElementById('dynamic-list');
        if (!listElement ||!listElement.contains(event.target)) {
            setListItems(prevItems => prevItems.map(item => ({...item, isActive: false })));
        }
    };

    useEffect(() => {
        const listElement = document.getElementById('dynamic-list');
        if (listElement) {
            listElement.addEventListener('click', (event) => {
                const itemId = parseInt(event.target.dataset.itemId);
                if (!isNaN(itemId)) {
                    handleListItemClick(itemId);
                }
            });
            return () => {
                listElement.removeEventListener('click', (event) => {
                    const itemId = parseInt(event.target.dataset.itemId);
                    if (!isNaN(itemId)) {
                        handleListItemClick(itemId);
                    }
                });
            };
        }
    }, []);

    useEventListener('click', handleOutsideClick);

    return (
        <div id="dynamic-list">
            {listItems.map(item => (
                <div key={item.id} data-item-id={item.id} style={{ backgroundColor: item.isActive? 'lightblue' : 'white' }}>
                    {item.text}
                </div>
            ))}
        </div>
    );
};

export default DynamicList;

事件绑定和解绑管理

  1. 自定义 useEventListener Hook:在 useEffect 中使用 addEventListener 绑定事件,并返回一个函数,在组件卸载时通过 removeEventListener 解绑事件。
  2. 列表项点击事件:在 useEffect 中为列表元素绑定点击事件,并在返回函数中解绑事件,确保在组件卸载时不会有残留的事件监听。
  3. 列表外点击事件:通过 useEventListener 监听 document 的点击事件,同样会在组件卸载时自动解绑事件,避免内存泄漏。