React对接Laravel API,实现商机与账户数据关联显示账户名称
解决商机与账户关联显示账户名称的问题
你的核心问题是直接访问数组accounts的nome属性,这是错误的——accounts是数组,需要根据商机的account_id匹配对应的账户对象,才能拿到账户名称。下面是具体的修复方案:
基础修复方案(直接匹配)
在渲染每一条商机时,使用数组的find方法,根据account_id从accounts数组中找到对应的账户,再提取名称。同时处理找不到账户的异常情况(比如账户已删除但商机未清理):
修改表格tbody中对应<td>的代码:
<tbody> {opportunities.map((opportunity) => { // 根据account_id找到对应账户 const matchedAccount = accounts.find(acc => acc.id === opportunity.account_id); return ( <tr key={opportunity.id}> <th>{opportunity.id}</th> <td>{opportunity.nome}</td> <td>{opportunity.account_id}</td> {/* 显示账户名称,找不到则显示默认文本 */} <td>{matchedAccount ? matchedAccount.nome : "未关联账户"}</td> <td>{opportunity.estagioNome}</td> <td>{opportunity.dataApresentacao}</td> <td className="text-center"> <Link to={`edit/${opportunity.id}`} className="btn btn-outline-warning" > Editar </Link> <button onClick={() => deleteOpportunity(opportunity.id)} className="btn btn-outline-danger m-1" > Deletar </button> </td> </tr> ); })} </tbody>
性能优化方案(大数据量适用)
如果账户数据较多,find方法每次渲染都会遍历数组,性能会受影响。可以用useMemo将账户数组转换成ID-账户对象的映射,这样查找账户的时间复杂度从O(n)降到O(1):
- 导入
useMemo:
import { useState, useEffect, useMemo } from "react";
- 在组件内部生成账户映射:
const OpportunityShow = () => { const [opportunities, setOpportunities] = useState([]); const [accounts, setAccounts] = useState([]); // 生成ID到账户的映射,仅在accounts变化时重新计算 const accountMap = useMemo(() => { return accounts.reduce((map, account) => { map[account.id] = account; return map; }, {}); }, [accounts]); // ... 其余代码不变
- 渲染时直接通过映射获取账户:
<td>{accountMap[opportunity.account_id]?.nome || "未关联账户"}</td>
额外优化建议
- 合并两个
useEffect,减少重复渲染:
useEffect(() => { const fetchData = async () => { await Promise.all([getAllOpportunities(), getAllAccounts()]); }; fetchData(); }, []);
这样可以同时发起两个API请求,避免两次独立的渲染触发。
- 移除无用的
value属性:原代码中<td value={opportunity.account_id}>的value属性对表格单元格无效,可以直接删掉。
修改后的完整组件代码
import Table from "react-bootstrap/Table"; import axios from "axios"; import { Link } from "react-router-dom"; import { useState, useEffect, useMemo } from "react"; import Form from "react-bootstrap/Form"; import Button from "react-bootstrap/Button"; const endpoint = "http://localhost:8000/api"; const endpointAccount = "http://localhost:8000/api"; const OpportunityShow = () => { const [opportunities, setOpportunities] = useState([]); const [accounts, setAccounts] = useState([]); // 生成账户ID映射表 const accountMap = useMemo(() => { return accounts.reduce((map, account) => { map[account.id] = account; return map; }, {}); }, [accounts]); useEffect(() => { const fetchData = async () => { await Promise.all([getAllOpportunities(), getAllAccounts()]); }; fetchData(); }, []); const getAllAccounts = async () => { const response = await axios.get(`${endpointAccount}/accounts`); setAccounts(response.data); }; const getAllOpportunities = async () => { const response = await axios.get(`${endpoint}/opportunities`); setOpportunities(response.data); }; const deleteOpportunity = async (id) => { await axios.delete(`${endpoint}/opportunity/${id}`); getAllOpportunities(); }; return ( <div className="d-grid gap-2"> <div className="row"> <div className="col-8"> <Form className="d-flex m-1"> <Form.Control type="search" placeholder="Filtro" className="me-2" aria-label="Search" /> <Button variant="outline-secondary">Search</Button> </Form> </div> <div className="col-4"> <Link to="/opportunity/create" className="col-11 btn btn-outline-primary m-1 " > Create </Link> </div> </div> <Table hover className=""> <thead> <tr> <th scope="col">#</th> <th scope="col">Nome</th> <th scope="col">Conta id</th> <th scope="col">Conta nome</th> <th scope="col">Fase</th> <th scope="col">Data Fechamento</th> <th className="text-center" scope="col"> Ações </th> </tr> </thead> <tbody> {opportunities.map((opportunity) => ( <tr key={opportunity.id}> <th>{opportunity.id}</th> <td>{opportunity.nome}</td> <td>{opportunity.account_id}</td> <td>{accountMap[opportunity.account_id]?.nome || "未关联账户"}</td> <td>{opportunity.estagioNome}</td> <td>{opportunity.dataApresentacao}</td> <td className="text-center"> <Link to={`edit/${opportunity.id}`} className="btn btn-outline-warning" > Editar </Link> <button onClick={() => deleteOpportunity(opportunity.id)} className="btn btn-outline-danger m-1" > Deletar </button> </td> </tr> ))} </tbody> </Table> </div> ); }; export default OpportunityShow;
内容的提问来源于stack exchange,提问作者Coimbra
相关产品推荐
相关产品推荐

