简体   繁体   中英

React - Updating previous array with new values using useState hook

I created a home page that contains three components HomeHeader , CompanyList and ScrollToTopBtn . Focusing on CompanyList it is a container component that shows list of CompanyCards that were fetched using an API in Home page. List of companies are initialized in Home page like this

const [companies, setCompanies] = useState([]);

The problem is that initially after making the API call, I use spread operator to update the companies list like this

const fetchedCompanies = await fetchFeedData(page);
console.log(fetchedCompanies);
setCompanies((prevCompanies=>{return [...prevCompanies,fetchedCompanies]}));
console.log(companies);

But an error occurs Uncaught TypeError: prevCompanies is not iterable since I believe that companies list is initially empty.

I tried to use another approach by using concat method but companies list stayed empty and showed no companies found message.

Below is the source code for Home page

const Home = () => {
    const [page, setPage] = useState(1); //In order to avoid showing "no companies found" initially
    const [isLoading, setIsLoading] = useState(true);
    const [companies, setCompanies] = useState([]);
    const [isError, setIsError] = useState(false);
    const [hasMore, setHasMore] = useState(false);
    useEffect(() => {
        const fetchData = async () => {
            setIsLoading(true);
            setIsError(false);
            try {
                const fetchedCompanies = await fetchFeedData(page);
                setCompanies((prevCompanies=>{prevCompanies.concat(fetchedCompanies)})); //problem here
                setHasMore(fetchedCompanies.length > 0)
            } catch (e) {
                setIsError(true)
                setHasMore(false);
            }
            setIsLoading(false)
        }
        fetchData();
    }, [page])
    return (
        <div>
            <ScrollToTopBtn />
            <Helmet>
                <title>{stackinfo.title} - {home.title}</title>
            </Helmet>
            <HomeHeader />
            {
                isLoading ? (<LoadingSpinner />) :
                    isError ? (<ErrorBoundary message={somethingWrongError.message}></ErrorBoundary>) :
                        <CompanyList companies={companies} ></CompanyList>
            }
            <Container className={"mt-5"}>
                <Row>
                    <Col sm={12}>
                        {(!isLoading && !isError && hasMore) && 
                        <CustomBtn
                            ButtonText={showMoreBtnText.message}
                            onClick={() => { console.log("Need to increment number of pages") }}
                        ></CustomBtn>}
                    </Col>
                </Row>
            </Container>
        </div>
    )
}

I tried to check the fetchedCompanies and companies after making the API call

const fetchedCompanies = await fetchFeedData(page);
//Returning new array containing previous companies and recent fetchedCompanies
console.log(fetchedCompanies);
setCompanies((prevCompanies=>{prevCompanies.concat(fetchedCompanies)}));
console.log(companies);

fetchedCompanies logged an array that has 9 elements, while companies as mentioned above logged empty array [] .

Sorry if I missed something, I am still new to React.

You can do this below to update

setCompanies([...companies, ...fetchedCompanies])

If fetched Company are totally new Array containing all with previous record then just do it below;

setCompanies([...fetchedCompanies]);
//OR
setCompanies(fetchedCompanies);

If you have empty strings then do this below

setCompanies([...companies, ...fetchedCompanies.filter(com => !com)]);

This is trying to mutate state, not return a new state (or maybe you just forgot the return keyword?):

setCompanies((prevCompanies=>{prevCompanies.concat(fetchedCompanies)}));

Your previous attempt was closer:

setCompanies(prevCompanies=>{return [...prevCompanies, fetchedCompanies]});

But if fetchedCompanies is also an array as the name implies then you forgot its spread operator:

setCompanies(prevCompanies=>{return [...prevCompanies, ...fetchedCompanies]});

Without that the resulting array would be weird at best.

You can also simplify a little:

setCompanies(prevCompanies=>[...prevCompanies, ...fetchedCompanies]);

And if you don't expect these calls to overlap at all, you could potentially simplify a lot:

setCompanies([...companies, ...fetchedCompanies]);

If after that there is still an empty string then it seems that the data has an empty string. In that case you'd have to filter that out manually, and where you do that is really up to you if the act of filtering might mess with the rest of the logic you have there (the hasMore value, for example). But you can append .filter() to the resulting array any time you like. When fetching, when updating state, or even just when rendering.

You would be better to look where the empty string is coming from and try to resolve that issue but if you simple want to remove the empty string, use the following.

prevCompanies.concat(fetchedCompanies).filter(Boolean)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM