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