Can someone explain, why I cant run the JavaScript map function on a React state hook?
const [sequenceNames, setSequenceNames] = useState(null);
useEffect(() => {
fetch('/savedFiles')
.then(response => response.json())
.then(data => setSequenceNames(data));
}, []);
const table = sequenceNames.map(name => Sequence(name));
This works with a for in loop, but my linter prohibits the use of for in.
const table = [];
for (const name in sequenceNames) {
table.push(Sequence(sequenceNames[name]));
}
When I use .map though I get the following error.
TypeError: Cannot read property 'map' of null
at main.a21158832f7ed8c55e25.bundle.js:1
at Bi (main.a21158832f7ed8c55e25.bundle.js:1)
at main.a21158832f7ed8c55e25.bundle.js:1
at f (main.a21158832f7ed8c55e25.bundle.js:1)
at d (main.a21158832f7ed8c55e25.bundle.js:1)
at main.a21158832f7ed8c55e25.bundle.js:1
even though my sequenceNames
array should not be null.
Can someone explain, why I cant run the JavaScript map function on a React state hook?
const [sequenceNames, setSequenceNames] = useState(null);
useEffect(() => {
fetch('/savedFiles')
.then(response => response.json())
.then(data => setSequenceNames(data));
}, []);
const table = sequenceNames.map(name => Sequence(name));
This works with a for in loop, but my linter prohibits the use of for in.
const table = [];
for (const name in sequenceNames) {
table.push(Sequence(sequenceNames[name]));
}
When I use .map though I get the following error.
TypeError: Cannot read property 'map' of null
at main.a21158832f7ed8c55e25.bundle.js:1
at Bi (main.a21158832f7ed8c55e25.bundle.js:1)
at main.a21158832f7ed8c55e25.bundle.js:1
at f (main.a21158832f7ed8c55e25.bundle.js:1)
at d (main.a21158832f7ed8c55e25.bundle.js:1)
at main.a21158832f7ed8c55e25.bundle.js:1
even though my sequenceNames
array should not be null.
2 Answers
Reset to default 3The issue you are having is that you are setting the initial state of sequenceNames
null
, so you are running into a race condition, where the first time you try and run a loop over sequenceNames
it is null
. You could initialize sequenceNames
to an empty array []
, but the best way to acplish what you are looking to do is by embracing hooks.
import React, { useMemo, useState } from 'react';
import { Sequence } from '.';
export interface DisplayNamesProps {
id?: number;
}
export function DisplayNames(props: DisplayNamesProps) {
const { id } = props;
const [sequenceNames, setSequenceNames] = useState<Sequence[]>([]);
// Use useEffect with the 2nd param, to guard against `fetch`
// executing on each render. In this example I use `id` as
// a var that should be unique for each `fetch` call.
useEffect(() => {
fetch(`/savedFiles/${id}`)
.then(response => response.json())
.then(data => setSequenceNames(data));
}, [id]);
// Create a memoized var `table` that updates when there
// is a change to `sequenceNames`.
const table = useMemo(() => {
return sequenceNames.map(name => Sequence(name))
}, [sequenceNames]);
return <div>{table}</div>;
}
Change
const table = sequenceNames.map(name => Sequence(name));
To
const table = Array.isArray(sequenceNames) && sequenceNames.map(name => Sequence(name));