React函数组件调用外部函数时State方法传参为undefined问题
React外部函数接收参数为undefined的原因及解决办法
问题描述
我正在构建一个React函数组件,该组件使用多个state变量,尝试通过按钮点击事件调用外部函数Load来修改这些变量,但传入该外部函数的state方法及自定义getter函数全部为undefined。若将相同代码直接写在函数组件内部则完全正常。
Core组件代码
import React from "react"; import {CodeArea, Table, EmptyField, Button} from '../util/util.js' import {Step, Load} from "./buttons.js" // 涉及的外部函数Load function Core(props){ const registersInitial = new Array(32).fill(0); let buttonNamesInitial = ['LOAD','play', 'step-forward', 'run-to', 'step-back','pause','halt', 'rerun', 'add']; const [bigText, setText] = React.useState(); const [registers, setRegisters] = React.useState(registersInitial); const [running, setRunning] = React.useState(false); const [programCounter, setProgramCounter] = React.useState(0); const [buttonNames, setButtonNames] = React.useState(buttonNamesInitial); const [lines, setLines] = React.useState([]); const getBigText = () => { return bigText; } const getRunning = () =>{ return running; } const getButtonNames = () => { return buttonNames; } //... 此处为无关代码 function getQuickbarContents(){ let functions = [ // 后续将对接后端 () => Load(setRunning, getRunning, setButtonNames, getButtonNames, setProgramCounter, setLines, getBigText), // 调用Load的位置 () => console.log("code running..."), () => console.log("stepping to next line..."), () => console.log("running to location..."), () => console.log("stepping..."), () => console.log("pausing..."), () => console.log("halting..."), () => console.log("running again..."), () => console.log("select widget to add...") ] let contents = []; let row = []; for (let i = 0; i<9; i++){ row.push(<Button onClick ={functions[i]} className='quickbar' name={buttonNames[i]}/>); contents.push(row); row = []; } return contents } const divs = []; let buttons = getQuickbarContents(); divs.push(<div key='left' className='left'><Table name='quickbar' rows='7' cols='1' fill={buttons}/> </div>); //... 此处为添加更多组件的代码 return divs; } export default Core;
Button组件代码
function Button({onClick, className, name}){ return <button onClick={onClick} className={className} name={name}>{name}</button> }
Load函数代码
export function Load({setRunning, getRunning, setButtonNames, getButtonNames, setProgramCounter, setLines, getBigText}){ let newButtonName; if (!getRunning()){ // 此处报错:getRunning()为undefined newButtonName = "Reload"; } else{ // 运行中用户点击重新加载 newButtonName = "LOAD"; } let lines = getBigText().split(/\n/); setLines(lines); setProgramCounter(0); setRunning(!getRunning()); const newButtonNames = getButtonNames().map((value, index) =>{ if (index === 0){ return (newButtonName); } return value; }) setButtonNames(newButtonNames); }
调试发现传入Load的所有参数在执行时均为undefined,但将Load的代码直接写在组件内部则一切正常。
原因分析
核心问题是参数传递方式与函数定义不匹配:
Load函数定义时使用了对象解构参数({setRunning, getRunning, ...}),意味着它期望接收一个包含这些属性的单个对象作为参数。- 但调用
Load时,传入的是多个独立的参数(Load(setRunning, getRunning, ...)),而非一个对象。此时函数内部尝试从第一个参数(即setRunning函数)中解构setRunning、getRunning等属性,自然会得到undefined。
解决方法
方法一:修改调用方式,传入对象参数
将调用Load时的多个独立参数打包成一个对象,匹配函数的解构定义:
() => Load({ setRunning, getRunning, setButtonNames, getButtonNames, setProgramCounter, setLines, getBigText })
方法二:修改Load函数定义,接收独立参数
去掉函数参数的对象解构,直接接收多个独立参数:
export function Load(setRunning, getRunning, setButtonNames, getButtonNames, setProgramCounter, setLines, getBigText){ let newButtonName; if (!getRunning()){ newButtonName = "Reload"; } else{ newButtonName = "LOAD"; } let lines = getBigText().split(/\n/); setLines(lines); setProgramCounter(0); setRunning(!getRunning()); const newButtonNames = getButtonNames().map((value, index) =>{ if (index === 0){ return (newButtonName); } return value; }) setButtonNames(newButtonNames); }
额外优化:移除不必要的getter函数
实际上不需要封装getRunning、getBigText这类getter函数,直接将state变量传递给外部函数即可,既能简化代码,也避免冗余:
修改后的Load函数:
export function Load({setRunning, running, setButtonNames, buttonNames, setProgramCounter, setLines, bigText}){ const newButtonName = !running ? "Reload" : "LOAD"; const lines = bigText.split(/\n/); setLines(lines); setProgramCounter(0); setRunning(!running); const newButtonNames = buttonNames.map((value, index) => index === 0 ? newButtonName : value); setButtonNames(newButtonNames); }
调用方式改为:
() => Load({ setRunning, running, setButtonNames, buttonNames, setProgramCounter, setLines, bigText })
内容的提问来源于stack exchange,提问作者user15448116
相关产品推荐
相关产品推荐

