简体   繁体   中英

React Redux state array variable pass as prop to child component, either infinite loop or empty array

I am passing Redux state variable collection as props to PokeList.js, using the useEffect hook to set state, but if I set props.collection as dependency the array map function sees the props.collection as empty array, since I tried to log out pokeData in the map function and gets nothing, if I remove the props.collection dependency, it creates an infinite loop situation, I can console.log the props.collection, which shows an empty array first, then the correct array, how do I set the state correctly?

I have tried dispatching in PokeList.js, which has the same result, also tried dirrectly initialize cardList = props.collection.map... but get cardList undefined, also tried using React.memo and set props as dependency doesn't work either

//PokeList.js
import React, { useState, useEffect } from 'react';
import Card from 'react-bootstrap/Card';
import ListGroup from 'react-bootstrap/ListGroup';

const PokeList = (props) => {
    const [cardList, setCardList] = useState();
    console.log(props.collection)

    useEffect(() => {
        var newCardList = props.collection.map(pokeData => { 
            console.log(pokeData)
            return (
                <Card key={pokeData.id} style={{ width: '18rem' }}>
                    <Card.Img variant="top" src={pokeData.sprite} />
                    <Card.Body>
                        <Card.Title>{pokeData.Name}</Card.Title>
                        <ListGroup className="list-group-flush">
                            <ListGroup.Item>{'Height: ' + pokeData.height}</ListGroup.Item>
                            <ListGroup.Item>{'Weight: ' + pokeData.weight}</ListGroup.Item>
                        </ListGroup>
                    </Card.Body>
                </Card>
            )})
        setCardList(newCardList)
    }, [props.collection])

    return (
        <div>
           {cardList}
        </div>
    )
}

export default PokeList;

//Search.js
import React, { useEffect } from 'react';
import { Container } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';

import PokeList from './pokedex/PokeList';
import * as pokedexActions from './pokedex/actions/PokedexActions';

const Search = () => {
    const dispatch = useDispatch();

    useEffect(() => {
        dispatch(pokedexActions.getLimitNames(5))
    }, [dispatch])

    const collection = useSelector(state => state.pokedex.collection);

    return (
        <div>
            <Container>
                <h2>Search</h2>
                <PokeList collection={collection}/>
            </Container>
        </div>
    );
}

export default Search;

// reducer.js
import { GET_LIMIT_NAMES } from '../actions/PokedexActions';

const initialState = {
    collection: []
};

export default (state = initialState, action) => {
    switch (action.type) {
        case GET_LIMIT_NAMES:
            return {
                collection: action.data
            };
        default:
            return state;
    }
};
// action.js
import Pokemon from '../Pokemon';

export const GET_LIMIT_NAMES = "GET_LIMIT_NAMES";

export const getLimitNames = (limit = 100) => {
    // redux-thunk
    return async dispatch => {
        try {
            const allResponse = await fetch(`https://pokeapi.co/api/v2/pokemon/?limit=${limit}`);
            const allUrlsData = await allResponse.json();
            // console.log(allUrlsData.results);

            const collection = [];

            Promise.all(allUrlsData.results.map(urlData => {
                var pokemon;
                fetch(urlData.url).then(resp =>
                    resp.json()
                ).then(data => {
                    // console.log(data);
                    pokemon = new Pokemon(data);
                    // pokemon.log();
                    collection.push(pokemon)
                }).catch(err => {
                    console.log(err);
                })
                return collection;
            }))

            // console.log(collection)

            dispatch({
                type: GET_LIMIT_NAMES,
                data: collection
            });

        } catch (err) {
            console.log(err);
        }
    };
};

if I try to directly render the map result, nothing appeared, the map function still only gets the empty array,

// PokeList.js
import React from 'react';
import Card from 'react-bootstrap/Card';
import ListGroup from 'react-bootstrap/ListGroup';
import './Pokedex.css'

const PokeList = (props) => {
    console.log('props.collection', props.collection)

    return (
        // <div className='poke-list'>
        //    {cardList}
        // </div>
        <div className='poke-list'>
            {props.collection.map(pokeData => {
                return (
                    <Card key={pokeData.id} style={{ width: "18rem" }}>
                        <Card.Img variant="top" src={pokeData.sprite} />
                        <Card.Body>
                            <Card.Title>{pokeData.Name}</Card.Title>
                            <ListGroup className="list-group-flush">
                                <ListGroup.Item>{"Height: " + pokeData.height}</ListGroup.Item>
                                <ListGroup.Item>{"Weight: " + pokeData.weight}</ListGroup.Item>
                            </ListGroup>
                        </Card.Body>
                    </Card>
                );
            })}
        </div>
    )
}

export default PokeList;

在此处输入图像描述

if I remove the [props.collection] dependency, it creates the infinite loop situation, but the components are rendering在此处输入图像描述

The issue here is that every time Search renders, collection is created and passed to PokeList . Then PokeList is using that collection (new on every render, remember) and using it as a dependency of your hook. This means that every time Search renders, the hook in PokeList will run.

Simply use collection prop to render the Component tree inside PokeList :

const PokeList = props => {
    console.log(props.collection);

    return (
        <div>
            {props.collection.map(pokeData => {
                return (
                    <Card key={pokeData.id} style={{ width: "18rem" }}>
                        <Card.Img variant="top" src={pokeData.sprite} />
                        <Card.Body>
                            <Card.Title>{pokeData.Name}</Card.Title>
                            <ListGroup className="list-group-flush">
                                <ListGroup.Item>{"Height: " + pokeData.height}</ListGroup.Item>
                                <ListGroup.Item>{"Weight: " + pokeData.weight}</ListGroup.Item>
                            </ListGroup>
                        </Card.Body>
                    </Card>
                );
            })}
        </div>
    );
};

turns out the problem is in Acions.js Problem.all by changing into for loop and await for all fetches solves the not empty array problem

for (var i=0; i<allUrlsData.results.length; i++) {
                const res = await fetch(allUrlsData.results[i].url)
                const resData = await res.json()
                const pokemon = new Pokemon(resData)
                collection.push(pokemon)
            }

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