You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React函数式组件中如何切换作为变量传入的按钮的className?

React按钮className切换失败问题解决

核心错误点

  • 混淆了外部传入的原生DOM按钮和组件内部返回的React按钮,两者逻辑完全脱节
  • useState异步更新逻辑错误:在setState的updater函数中重复调用同状态的set方法,且直接使用更新前的旧状态值赋值给DOM,导致类名无法同步
  • 事件传参错误:给原生DOM绑定点击事件时,回调函数的第一个参数是事件对象,并非你预期的按钮DOM实例
  • 组件返回的冗余按钮无实际作用:绑定的launchButton.toggle_get_places在原生DOM上不存在,无任何交互逻辑

修正方案

方案1:保留外部传入原生按钮的逻辑(无需组件内部返回按钮)

import React from 'react';
import { useInput } from '../hooks/useInput';
import {
    placesSource,
    places_lyr,
} from "../qgis2web/layers";
import {
    increaseThrobberCounter,
    decreaseThrobberCounter
} from "../qgis2web/throbberCounter";
import { addLayer, getErrorMessage, removeLayer } from "../qgis2web/qgis2web";
import { add_geojson_feats } from "../qgis2web/addGeojsonFeatures";

declare const ol, $, toastr;

const GetPlacesControl = ({ map, launchButton }) => {
    const [btnActive, setBtnState] = React.useState<boolean>(false);

    function clear_places_features(map) {
        placesSource.clear();
        removeLayer(places_lyr);
    }

    const toggle_get_places = () => {
        // 直接更新激活状态,不要嵌套setState
        setBtnState(prev => !prev);
    }

    const runCallback = React.useCallback((evt) => {
        clear_places_features(map);
        increaseThrobberCounter(); // 补全遗漏的计数增加逻辑
        $.ajax({
            type: "POST",
            contentType: "application/json;charset=utf-8",
            url: "api/get_places?x=" + evt.coordinate[0] + "&y=" + evt.coordinate[1],
            traditional: "true",
            datatype: "json",
            mimeType: 'application/json',
        }).done(function (response) {
            const places_feats = new ol.format.GeoJSON().readFeatures(response);
            if (places_feats.length == 0) {
                toastr.warning('no records found, try again');
            } else {
                placesSource.addFeatures(places_feats);
                map.getView().fit(placesSource.getExtent());
                places_lyr.setVisible(true);
                addLayer(places_lyr);
            }
        }).fail(function (jqXHR) {
            let msg = getErrorMessage(jqXHR);
            toastr.error(msg);
        }).always(function () {
            decreaseThrobberCounter();
        });
    }, [map]);

    // 监听btnActive状态变化,同步更新外部按钮类名和地图交互
    React.useEffect(() => {
        if (btnActive) {
            launchButton.className = "fas fa-ban";
            $('html,body').css('cursor', 'crosshair');
            map.on('click', runCallback);
        } else {
            launchButton.className = "fas fa-globe-americas";
            $('html,body').css('cursor', 'default');
            map.un('click', runCallback);
            clear_places_features(map);
        }
    }, [btnActive, map, runCallback, launchButton]);

    React.useEffect(() => {
        const get_coords_on_click = (evt) => evt.coordinate;
        launchButton.addEventListener('click', toggle_get_places, false);
        map.on('singleclick', get_coords_on_click);

        return () => {
            launchButton.removeEventListener('click', toggle_get_places);
            map.un('singleclick', get_coords_on_click);
            // 组件销毁时重置样式和事件
            $('html,body').css('cursor', 'default');
            map.un('click', runCallback);
        };
    }, [launchButton, map, runCallback]);

    // 不需要返回额外按钮,直接使用外部传入的按钮
    return null;
};

export default GetPlacesControl;

方案2:更符合React规范的写法(不需要外部传入按钮,组件内部渲染)

父组件调用时直接传map参数即可,不需要提前创建按钮:

const GetPlacesControl = ({ map }) => {
    const [btnActive, setBtnState] = React.useState<boolean>(false);
    // 其余逻辑和方案1完全一致,仅替换返回的组件内容
    // ...省略重复逻辑

    return (
        <button 
            className={btnActive ? 'fas fa-ban' : 'fas fa-globe-americas'}
            onClick={toggle_get_places}
        />
    );
};

注意事项

  • React的状态更新是批量异步的,不要在setState的updater函数里调用同个state的set方法,也不要在状态更新后立刻读取该状态值,应该通过useEffect监听状态变化执行后续逻辑
  • 尽量不要直接操作原生DOM的属性,优先通过React的状态绑定DOM属性,符合单向数据流的设计规范
  • useCallback的依赖项要补全,用到的外部变量都要放到依赖数组里,避免闭包陷阱

内容的提问来源于stack exchange,提问作者kflaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 06:27:03