如何基于 API 调用呈现下拉列表

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

我想知道如何在 Outlook 插件中使用 API 调用填充下拉列表后呈现它。它不会在我的屏幕上显示。

基本示例:

const App () => {
    let contactDropdown = <></>;

    (async () => {
        await Office.onReady();

        const contacts = await getContacts();
        contactDropdown = buildDropdown(contacts);
    })();

    const buildDropdown = (contacts) => {
        //e.g.:
        // contacts = [
        //     { value: 1, text: "Tim" },
        //     { value: 2, text: "Fred" }
        // ];

        return (
            <div>
                <label>Choose a Contact</label>
                <select>
                    {contacts.map((option) => (
                        <option key={option.value} value={option.value}>
                          {option.text}
                        </option>
                      ))}
                </select>
            </div>
          );
    };

    return (
        <div>
          {contactDropdown}
        </div>
    );
}

我需要先使用

useEffect
并声明加载数据吗?我尝试过这个,但它不起作用。

  const [contacts, setContacts] = useState([]);
  useEffect(() => {
    setContacts(getContacts());
  }, []);

  const getContacts = async () => {
    //api call here
  };
javascript reactjs jsx outlook-addin office-addins
1个回答
0
投票

首先,你不必等待获取数据来定义下拉组件。

其次,在尝试使用状态时,您将承诺存储在状态中,而不是实际结果。

这是概念证明:

const ContactsDropdown = () => {
  const [contacts, setContacts] = useState([])
  const [loading, setLoading] = useState(true)
  useEffect(() => {
    const fetchData = async () =>
      getContacts()
        .catch(console.error, [])
        .then((r) => {
          setLoading(false)
          setContacts(r) // or r.data!? (depends on server response)
        })
    fetchData()
  }, [])
  return (
    <div>
      {loading && <div>loading contacts...</div>}
      {!loading && !contacts.length && <div>no contacts loaded!</div>}
      {!loading && contacts.length && (
        <>
          <label>Choose a Contact</label>
          <select>
            {contacts.map(({ value, text }) => (
              <option key={value} value={value}>
                {text}
              </option>
            ))}
          </select>
        </>
      )}
    </div>
  )
}

为了简洁起见,上述组件获取数据,但这可能并不理想。

在获取联系人后,将该状态提升到父组件并将联系人作为道具传递到下拉菜单可能更有意义。

这可能是这样的(仍然是简单的,但演示了原理):

const { useState, useCallback, useEffect } = React
const limitValue = (min, max, val) => Math.min(max, Math.max(min, val))
const Dropdown = ({ contacts, selected, setSelected }) => {
  return (
    <select
      value={(selected && selected.id) || ''}
      onChange={({ target: { value } }) => {
        setSelected(contacts.find(({ id }) => id === +value))
      }}
    >
      <option>-- Select a contact --</option>
      {contacts.map((item) => (
        <option key={item.id} value={item.id}>
          {item.name}
        </option>
      ))}
    </select>
  )
}
const App = () => {
  const [contacts, setContacts] = useState([])
  const [selected, setSelected] = useState(null)
  const [loading, setLoading] = useState(false)
  useEffect(() => {
    setLoading(true)
    fetch('https://jsonplaceholder.typicode.com/users')
      .then((response) => response.json())
      .then((json) => setContacts(json))
      .then(() => setLoading(false))
  }, [])
  return (
    <React.Fragment>
      {loading && <div>contacts loading...</div>}
      {!loading && !contacts.length && <div>no contacts fetched!</div>}
      {!loading && contacts.length && (
        <React.Fragment>
          <Dropdown {...{ contacts, selected, setSelected }}></Dropdown>
          <pre>{JSON.stringify({ selected }, null, 2)}</pre>
        </React.Fragment>
      )}
    </React.Fragment>
  )
}
ReactDOM.createRoot(root).render(<App />)
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

© www.soinside.com 2019 - 2024. All rights reserved.