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

ReactJs手风琴仅点击头部加载数据、props传参报错问题排查

问题解决方法

问题根源

  1. 你使用全局变量存储customer数据,React 仅会在state变更时触发组件重渲染,全局变量赋值不会触发App组件更新,因此只有点击手风琴触发expandedSections state变更时,才会把最新的customer值传给Display组件。
  2. 异步请求存在竞态问题:你先调用client.get获取customer_id,紧接着就发起获取客户列表的请求,此时第一个异步请求还未返回,customer_id还是初始值null,大概率匹配不到目标客户数据。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:36:04