1

我想制作一个下拉列表,用户可以在其中从 Nextjs 的令牌列表中选择一个 erc20 令牌。

我在令牌列表上尝试了一个常规映射函数,但是站点没有响应并且非常慢,因为tokenlist.json。我想在视口中渲染数据。我怎样才能做到这一点?

我想让它变得更快,就像在 Uniswap中的令牌选择模式中一样

我使用了 nextjs Image 并在视图中加载了令牌图像,但它仍然很慢,因为它需要呈现令牌名称和符号

这就是我获取令牌列表并呈现它的方式:


import { Fragment, useEffect, useState } from 'react';
import { Combobox, Transition } from '@headlessui/react';
import { CheckIcon, SelectorIcon } from '@heroicons/react/solid';
import { PlusSmIcon } from '@heroicons/react/outline';
import axios from 'axios';
import tokensJson from '../web3/tokens.json';
import Image from 'next/image';

export default function SelectErc20() {
  const [selected, setSelected] = useState(tokensJson.tokens[0]);
  const [tokenlist, setTokenlist] = useState([]);
  const [query, setQuery] = useState('');

  const filteredTokens =
    query === ''
      ? tokenlist
      : tokenlist.filter((token) =>
          token.name
            .toLowerCase()
            .replace(/\s+/g, '')
            .includes(query.toLowerCase().replace(/\s+/g, ''))
        );

  useEffect(() => {
    axios
      .get('https://tokens.coingecko.com/uniswap/all.json')
      .then((res) => {
        setTokenlist(res.data.tokens);
      })
      .catch(setTokenlist(tokensJson.tokens));
  }, []);

  return (
    <div className="flex items-center space-x-3">
      <img src={selected.logoURI} alt="token" className="h-6 w-6" />
      <div className="w-64">
        <Combobox value={selected} onChange={setSelected}>
          <div className="relative mt-1">
            <div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-emerald-300 sm:text-sm">
              <Combobox.Input
                className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
                displayValue={(token) => token.name}
                onChange={(event) => setQuery(event.target.value)}
              />
              <Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
                <SelectorIcon
                  className="h-5 w-5 text-gray-400"
                  aria-hidden="true"
                />
              </Combobox.Button>
            </div>
            <Transition
              as={Fragment}
              leave="transition ease-in duration-100"
              leaveFrom="opacity-100"
              leaveTo="opacity-0"
            >
              <Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
                <a
                  href="#"
                  className="relative mb-3 flex select-none items-center space-x-3 py-2 px-4 text-gray-700 hover:bg-neutral-100"
                >
                  <PlusSmIcon className="h-5 w-5" />
                  <span>Add custom token</span>
                </a>
                {filteredTokens.length === 0 && query !== '' ? (
                  <div className="relative select-none py-2 px-4 text-gray-700">
                    <span>Nothing found..</span>
                  </div>
                ) : (
                  filteredTokens.map((token) => (
                    <Combobox.Option
                      key={token.address}
                      className={({ active }) =>
                        `relative cursor-default select-none py-2 pl-10 pr-4 ${
                          active ? 'bg-emerald-600 text-white' : 'text-gray-900'
                        }`
                      }
                      value={token}
                    >
                      {({ selected, active }) => (
                        <div className="flex items-center justify-between">
                          <div className="flex items-center truncate">
                            <Image
                              src={token.logoURI}
                              alt={token.name}
                              width="24"
                              height="24"
                              className="mr-3"
                            />
                            <span
                              className={`block truncate ${
                                selected ? 'font-medium' : 'font-normal'
                              }`}
                            >
                              {token.name}
                            </span>
                          </div>
                          <span
                            className={`block text-xs text-gray-400 ${
                              selected ? 'font-medium' : 'font-normal'
                            } ${active ? 'text-white' : null}`}
                          >
                            {token.symbol}
                          </span>
                          {selected ? (
                            <span
                              className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
                                active ? 'text-white' : 'text-emerald-600'
                              }`}
                            >
                              <CheckIcon
                                className="h-5 w-5"
                                aria-hidden="true"
                              />
                            </span>
                          ) : null}
                        </div>
                      )}
                    </Combobox.Option>
                  ))
                )}
              </Combobox.Options>
            </Transition>
          </div>
        </Combobox>
      </div>
    </div>
  );
}


4

1 回答 1

1

这是因为您渲染了太多的 HTML 节点,您的导航器无法绘制它。

为了做您需要的事情,您必须使用我们所说的“虚拟列表”。

虚拟化的库很少,您不是第一个。

以React Window为例

于 2022-02-24T19:02:19.867 回答