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();
