React如何将函数式组件转换为类组件?附代码示例与报错处理
React函数式组件转类组件修正指导
问题说明
正在尝试将React函数式组件转换为类组件,现有改写代码存在报错需要修正。
原始函数式组件代码
import React, { useEffect, useState } from "react" import ReactMapGL, { Marker, Popup } from "react-map-gl" import RsuMarker from './RsuMarker'; import mbStyle from '../styles/mb_style.json'; function Map(props) { const [viewport, setViewport] = useState({ latitude: 39.7392, longitude: -104.9903, width: 'calc(100% - 350px)', height: '100vh', zoom: 10 }); const [selectedRsu, setSelectedRsu] = useState(null); const [selectedRsuCount, setSelectedRsuCount] = useState(null); useEffect(() => { const listener = e => { if (e.key === "Escape") setSelectedRsu(null); }; window.addEventListener("keydown", listener); return () => { window.removeEventListener("keydown", listener); } }, []); return ( <div> <ReactMapGL {...viewport} mapboxApiAccessToken={process.env.REACT_APP_MAPBOX_TOKEN} mapStyle={mbStyle} onViewportChange={(viewport) => { setViewport(viewport); }}> {props.rsuData.map((rsu) => ( <Marker key={rsu.id} latitude={rsu.geometry.coordinates[1]} longitude={rsu.geometry.coordinates[0]}> <button className="marker-btn" onClick={(e) => { e.preventDefault(); setSelectedRsu(rsu); if (props.rsuCounts.hasOwnProperty(rsu.properties.Ipv4Address)) setSelectedRsuCount(props.rsuCounts[rsu.properties.Ipv4Address].count); else setSelectedRsuCount(0); }}> <RsuMarker onlineStatus={rsu.onlineStatus}/> </button> </Marker> ))} {selectedRsu ? ( <Popup latitude={selectedRsu.geometry.coordinates[1]} longitude={selectedRsu.geometry.coordinates[0]} onClose={() => { setSelectedRsu(null); setSelectedRsuCount(null); }}> <div> <h2 className="popop-h2">{selectedRsu.properties.Ipv4Address}</h2> <p className="popop-p">Online Status: {selectedRsu.onlineStatus}</p> <p className="popop-p">Milepost: {selectedRsu.properties.Milepost}</p> <p className="popop-p"> Serial Number: {selectedRsu.properties.SerialNumber ? selectedRsu.properties.SerialNumber : 'Unknown'} </p> <p className="popop-p">BSM Counts: {selectedRsuCount}</p> </div> </Popup> ) : null} </ReactMapGL> </div> ); } export default Map;
现有改写版本的报错
src\components\Map.js Line 24:36: 'viewport' is not defined no-undef Line 25:5: 'render' is not defined no-undef Line 30:21: 'viewport' is not defined no-undef Line 34:19: 'setViewport' is not defined no-undef Line 47:25: 'setSelectedRsu' is not defined no-undef Line 49:27: 'setSelectedRsuCount' is not defined no-undef Line 51:27: 'setSelectedRsuCount' is not defined no-undef Line 59:20: 'selectedRsu' is not defined no-undef Line 61:33: 'selectedRsu' is not defined no-undef Line 62:34: 'selectedRsu' is not defined no-undef Line 64:25: 'setSelectedRsu' is not defined no-undef Line 65:25: 'setSelectedRsuCount' is not defined no-undef Line 69:47: 'selectedRsu' is not defined no-undef Line 70:60: 'selectedRsu' is not defined no-undef Line 71:55: 'selectedRsu' is not defined no-undef Line 73:43: 'selectedRsu' is not defined no-undef Line 76:57: 'selectedRsuCount' is not defined no-undef
需要修正的核心点
- 语法结构错误:constructor未闭合导致render方法被定义在constructor内部,删除constructor里多余的
this.setState({'viewport': viewport});语句,同时删除无用的import {render} from 'react-dom';引入。 - state命名问题:初始state里的
SelectedRsu、SelectedRsuCount首字母大写,和使用时的小写不一致,统一修改为小写开头。 - 变量引用规则:类组件中所有state变量需要加
this.state.前缀,所有props变量需要加this.props.前缀,比如viewport改为this.state.viewport,props.rsuData改为this.props.rsuData。 - 状态更新规则:类组件没有useState返回的自定义更新函数,所有状态更新统一使用
this.setState()方法,比如更新viewport改为this.setState({viewport}),更新selectedRsu改为this.setState({selectedRsu: rsu})。 - 生命周期适配:原来useEffect实现的键盘监听,替换为
componentDidMount生命周期添加监听,componentWillUnmount生命周期移除监听。 - 属性名修正:React中class属性要改为className,所有
class="xxx"修改为className="xxx"。
修正后完整代码
import React, { Component } from 'react'; import ReactMapGL, { Marker, Popup } from "react-map-gl" import RsuMarker from './RsuMarker'; import mbStyle from '../styles/mb_style.json'; class Map extends Component { constructor(props) { super(props) this.state = { viewport: { latitude: 39.7392, longitude: -104.9903, width: 'calc(100% - 350px)', height: '100vh', zoom: 10 }, selectedRsu: null, selectedRsuCount: null, } } componentDidMount() { // 替换原来的useEffect添加监听 this.escListener = e => { if (e.key === "Escape") this.setState({selectedRsu: null}); }; window.addEventListener("keydown", this.escListener); } componentWillUnmount() { // 组件卸载时移除监听 window.removeEventListener("keydown", this.escListener); } render() { const { viewport, selectedRsu, selectedRsuCount } = this.state; return ( <div> <ReactMapGL {...viewport} mapboxApiAccessToken={process.env.REACT_APP_MAPBOX_TOKEN} mapStyle={mbStyle} onViewportChange={(viewport) => { this.setState({viewport}); }}> {this.props.rsuData.map((rsu) => ( <Marker key={rsu.id} latitude={rsu.geometry.coordinates[1]} longitude={rsu.geometry.coordinates[0]}> <button className="marker-btn" onClick={(e) => { e.preventDefault(); if (this.props.rsuCounts.hasOwnProperty(rsu.properties.Ipv4Address)) { this.setState({ selectedRsu: rsu, selectedRsuCount: this.props.rsuCounts[rsu.properties.Ipv4Address].count }) } else { this.setState({ selectedRsu: rsu, selectedRsuCount: 0 }) } }}> <RsuMarker onlineStatus={rsu.onlineStatus}/> </button> </Marker> ))} {selectedRsu ? ( <Popup latitude={selectedRsu.geometry.coordinates[1]} longitude={selectedRsu.geometry.coordinates[0]} onClose={() => { this.setState({ selectedRsu: null, selectedRsuCount: null }) }}> <div> <h2 className="popop-h2">{selectedRsu.properties.Ipv4Address}</h2> <p className="popop-p">Online Status: {selectedRsu.onlineStatus}</p> <p className="popop-p">Milepost: {selectedRsu.properties.Milepost}</p> <p className="popop-p"> Serial Number: {selectedRsu.properties.SerialNumber ? selectedRsu.properties.SerialNumber : 'Unknown'} </p> <p className="popop-p">BSM Counts: {selectedRsuCount}</p> </div> </Popup> ) : null} </ReactMapGL> </div> ); } } export default Map;
内容的提问来源于stack exchange,提问作者Poala
相关产品推荐
相关产品推荐

