Home > Back-end >  React context throwing TypeError: object is not iterable (cannot read property Symbol(Symbol.iterato
React context throwing TypeError: object is not iterable (cannot read property Symbol(Symbol.iterato

Time:09-04

I'm getting the error:

TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

whenever I try to manage state from useContext. The idea here is to allow for "tokens" to be initialised at [] on page load, then when set in the TokenListBox component, it is subsequently updated in TokenProviderContext.

TokenProviderContext.tsx:

const TokenProviderContext = React.createContext<any>([]);

export const TokenProvider = ({
  children,
}: {
  children:
    | ReactElement<React.ReactNode, string | JSXElementConstructor<unknown>>[]
    | ReactElement<React.ReactNode, string | JSXElementConstructor<unknown>>;
}) => {
  const [selectedTokens, setSelectedTokens] = useState<IToken[]>(sampleTokenList);

  const contextValue = useMemo(
    () => ({
      selectedTokens,
      setSelectedTokens,
    }),
    [selectedTokens, setSelectedTokens],
  );

  return <TokenProviderContext.Provider value={contextValue}>{children}</TokenProviderContext.Provider>;
};

export const useTokenProvider = () => useContext(TokenProviderContext);

TokenListBox.tsx:

export default function TokenListBox({ tokenList }: { tokenList: IToken[] }) {
  const [selectedTokens, setSelectedTokens] = useTokenProvider();

  useEffect(() => {
    if (!selectedTokens) {
      setSelectedTokens([]);
    }
  }, [selectedTokens, setSelectedTokens]);

  return (
    <Listbox value={selectedTokens} onChange={setSelectedTokens} multiple>
      {({ open }) => (
        <>
          <div className="relative mt-1">
            <Listbox.Button
              className="relative w-full cursor-default rounded-md border border-gray-300 bg-white
            py-2 pl-3 pr-10 text-left shadow-sm focus:border-sky-500 focus:outline-none focus:ring-1 focus:ring-sky-500
            sm:text-sm"
            >
              <span className="flex items-center">
                <span className="block truncate">Select Tokens</span>
              </span>
              <span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
                <ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
              </span>
            </Listbox.Button>
            <Transition
              show={open}
              as={Fragment}
              leave="transition ease-in duration-100"
              leaveFrom="opacity-100"
              leaveTo="opacity-0"
            >
              {tokenList.length > 0 && (
                <Listbox.Options
                  className="absolute z-10 mt-1 max-h-56 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"
                >
                  {tokenList.map((token) => (
                    <Listbox.Option
                      key={token.symbol}
                      className={({ active }) =>
                        classNames(
                          active ? 'text-white bg-sky-600' : 'text-gray-900',
                          'relative cursor-default select-none py-2 pl-3 pr-9',
                        )
                      }
                      value={token.name}
                    >
                      {({ selected, active }) => (
                        <>
                          <div className="flex items-center">
                            <img src={token.iconSrcUrl} alt="" className="h-6 w-6 flex-shrink-0 rounded-full" />
                            <span
                              className={classNames(selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate')}
                            >
                              {token.name}
                            </span>
                          </div>
                          {selected ? (
                            <span
                              className={classNames(
                                active ? 'text-white' : 'text-sky-600',
                                'absolute inset-y-0 right-0 flex items-center pr-4',
                              )}
                            >
                              <CheckIcon className="h-5 w-5" aria-hidden="true" />
                            </span>
                          ) : null}
                        </>
                      )}
                    </Listbox.Option>
                  ))}
                </Listbox.Options>
              )}
            </Transition>
          </div>
        </>
      )}
    </Listbox>
  );
}

CodePudding user response:

When you call useTokenProvider() you would get as result contextValue, which an object not an array, hence the error you are getting.

Assuming TokenListBox is wrapped in TokenProvider, this would work:

const {selectedTokens, setSelectedTokens} = useTokenProvider();
  • Related