使用 React 从不同端点异步获取 API 数据的最佳实践?

问题描述 投票:0回答:1

我正在为工作中的一个项目学习 React,目前在掌握异步模型方面确实遇到了困难。

这是我正在开发的组件:

const Customers = () => {
    const [logos, setLogos] = useState([]);
    const [customers, setCustomers] = useState([]);
    const [titlePrompt, setTitlePrompt] = useState([]);
    const navigate = useNavigate();

    useEffect(() => {
        // create a customer client object 
        const client = new Client();

        // query for the customer list and save name and token
        if (customers.length === 0) {
            client.get_customers().then((res) => {
                const customer_info = res.map(customer => {
                    return {
                        'name': customer.customer_name,
                        'token': customer.mqtt_customer_token
                    }
                });
                setCustomers(customer_info);
            });
        }

        if (customers.length > 0) {
            const fetched_logos = [];
            for (let i = 0; i < customers.length; i++) {
                client.get_site_by_type(customers[i].token, 1).then((res) => {
                    const new_logo = {
                        [customers[i].name]: res.logo 
                    };
                    fetched_logos.push(new_logo);
                });
            }
            setLogos(fetched_logos);
        }

        setTitlePrompt("Select A Customer Shown Below");
    }, [customers, logos]);

    /**
     * button handler to transition to a dataframe view of the chosen customer via token
     * @param {defining} token 
     */
    const onButtonClick = (token) => {
        navigate('/dataframe', { state: { token } });
    };

    return (
        <div className={'mainContainer'}>
            <div className={'contentContainer'}>
                <div className={'titleContainer'}>
                    <div>{titlePrompt}</div>
                </div>
                <br/>
                <ImageList cols={4}>
                    {customers.map(customer => (
                        <ImageListItem key={customer.name}>
                            <img alt={customer.name} src={`data:image/png;base64,${logos[customer.name]}`}></img>
                        </ImageListItem>
                    ))}
                </ImageList>
            </div>
        </div>
    );
};

export default Customers;

我想做的是获取客户信息,例如名称和徽标,并将其显示在 ImageList 中。

问题是,首先我必须从一个数据库中获取每个客户的名称和令牌(用于后续 API 调用),然后我需要一一获取徽标,因为它们都驻留在特定的客户数据库中。我想仅使用 img 组件的替代文本来渲染组件,然后在图像进入时填充它们。我只是不知道如何在 useEffect 中正确同步它。我也不知道使用 if 语句来控制后续 useEffect 调用期间执行的内容是否正确,或者只是黑客行为。

我尝试链接 .then 但 setState 调用是异步的,因此会导致竞争条件并且不起作用。

感谢您的阅读。

编辑:

这是一个代码片段,显示了我尝试链接 .then:

client.get_customers().then((res) => {
            const customer_info = res.map(customer => {
                return {
                    'name': customer.customer_name,
                    'token': customer.mqtt_customer_token
                }
            });
            setCustomers(customer_info);
        }).then(() => {
            for (let i = 0; i < customers.length; i++) {
                client.get_site_by_type(customers[i].token, 1).then((res) => {
                    const new_logo = {
                        [customers[i].name]: res[0].logo
                    }
                    setLogos([...logos, new_logo]);
                });
            }
        });
javascript reactjs react-hooks frontend
1个回答
0
投票

有两个问题,一是调用set函数后状态变量不会立即更新。 更好的是使用包含接收到的数据的变量(这里,我返回 customer_info,以便可以在

then
中使用它,而不是调用 setCustomers 然后期望 const 客户已更新,但事实并非如此) .

第二个问题是你需要使用promise.all或类似的来正确同步它。

未经测试的说明性代码:

client.get_customers().then((res) => {
  const customer_info = res.map(customer => {
    return {
      'name': customer.customer_name,
      'token': customer.mqtt_customer_token
    }
  });
  setCustomers(customer_info);
  return customer_info;
}).then((customer_info) => {
  const promises = customer_info.map(customer => 
    client.get_site_by_type(customer.token, 1).then((res) => {
      const new_logo = {
        [customer.name]: res[0].logo
      }
      return new_logo;
    })
  );
  Promise.all(promises).then(logos => setLogos(logos));
});
© www.soinside.com 2019 - 2024. All rights reserved.