ReactJs手风琴仅点击头部加载数据、props传参报错问题排查
问题解决方法
问题根源
- 你使用全局变量存储
customer数据,React 仅会在state变更时触发组件重渲染,全局变量赋值不会触发App组件更新,因此只有点击手风琴触发expandedSectionsstate变更时,才会把最新的customer值传给Display组件。 - 异步请求存在竞态问题:你先调用
client.get获取customer_id,紧接着就发起获取客户列表的请求,此时第一个异步请求还未返回,customer_id还是初始值null,大概率匹配不到目标客户数据。 - Display组件初始化时
tryData还未拿到接口数据,值为null,直接访问tryData.attributes会抛出类型错误。
修改方案
第一步:修改App.js逻辑
将全局变量替换为组件state,调整异步请求顺序,数据请求成功后更新state触发重渲染:
// App.js 完整修改后核心代码 import { useState, useEffect } from 'react'; // 保留原有其他引入项 const App = () => { const [expandedSections, setExpandedSections] = useState([]); const [expandedSections2, setExpandedSections2] = useState([]); // 用state存储客户数据和client实例,替换全局变量 const [customer, setCustomer] = useState(null); const [client, setClient] = useState(null); const getCustomerData = (clientInstance) => { // 把客户列表请求放到获取customer_id的回调中,保证请求顺序 clientInstance.get('ticket.requester.id').then( function(data) { const customer_id = data['ticket.requester.id'].toString(); const settings = { url:'/api/sunshine/objects/records?type=Customer', type:'GET', dataType: 'json', }; clientInstance.request(settings).then( function(res) { // 匹配目标客户 const targetCustomer = res.data.find(cust => cust.attributes.CustomerID === customer_id); // 更新state,触发组件重渲染,自动将最新数据传给Display if(targetCustomer) setCustomer(targetCustomer); }, function(response) { console.error(response.responseText); } ); } ); } useEffect(() => { const clientInstance = window.ZAFClient.init(); setClient(clientInstance); getCustomerData(clientInstance); }, []); // 原有渲染逻辑保持不变 return ( <Row justifyContent="center"> <Col className="outer_column"> <Accordion className="first_accordion" level={2} expandedSections={expandedSections} onChange={index => { if (expandedSections.includes(index)) { setExpandedSections(expandedSections.filter(n => n !== index)); } else { setExpandedSections([index]); } }} > <Accordion.Section> <Accordion.Header> <Accordion.Label>Customer Management</Accordion.Label> </Accordion.Header> <Accordion.Panel> <Row justifyContent="center"> <Col className="inner_column"> <Accordion isCompact level={5} isExpandable className="second_accordion" > <Accordion.Section> <Accordion.Header> <Accordion.Label>Customer Information</Accordion.Label> </Accordion.Header> <Accordion.Panel className="accordion_panel"> <Display jsonData = {JsonData} tryData = {customer}/> </Accordion.Panel> </Accordion.Section> </Accordion> </Col> </Row> </Accordion.Panel> </Accordion.Section> </Accordion> </Col> </Row> ) } export default App;
第二步:修改Display.js逻辑
处理初始tryData为null的情况,监听props变化自动更新state:
// Display.js 完整修改后核心代码 function withMyHook(Component) { return function WrappedComponent(props) { const myHookValue = useZafClient(); return <Component {...props} myHookValue={myHookValue} />; } } class Display extends React.Component { constructor(props) { super(props); // 优先使用tryData,数据未返回时用jsonData兜底,加可选链避免空值报错 const initAttributes = props.tryData?.attributes || props.jsonData.data[0].attributes; this.state = { fields: initAttributes, errors: {}, customers: [], flag: initAttributes.CustomerID === "" ? 'register' : 'view' }; } // 监听tryData变化,数据返回后更新state componentDidUpdate(prevProps) { if(this.props.tryData && !prevProps.tryData) { const attributes = this.props.tryData.attributes; this.setState({ fields: attributes, flag: attributes.CustomerID === "" ? 'register' : 'view' }) } } handleChange(field, e) { let fields = this.state.fields; fields[field] = e.target.value; this.setState({ fields }); } render() { // 数据未加载完成时显示加载状态,避免渲染报错 if(!this.props.tryData) return <div>数据加载中...</div> return( <div> {Object.keys(this.state.fields).map((key, i) => ( <p key={i}> <span>{key} <input value={this.state.fields[key] || ''} placeholder={key} disabled = {this.state.flag === 'view' ? "disabled" : "" } onChange={this.handleChange.bind(this,key)} /></span> </p> ))} {this.state.flag === "register" ? <Button onClick={() => this.setState({flag: 'view'})}> Register </Button> : null } {this.state.flag === "view" ? <Button onClick={() => this.setState({flag: 'update'})}> Edit </Button> : null } {this.state.flag === "update" ? <Button onClick={() => this.setState({flag: 'view'})}> Submit </Button> : null } </div> ) } }; export default withMyHook(Display);
内容的提问来源于stack exchange,提问作者Lucky LLemos
相关产品推荐
相关产品推荐

