Я провожу свои первые несколько экспериментов с React, и в этом компоненте я вызываю внешний API, чтобы получить список всех игроков NBA, отфильтровать их по teamId, который был получен как опора компонента, и, наконец, отобразить разметку отфильтрованных игроков. .

Одно из соображений заключается в том, что, поскольку я вызываю API и получаю большой список, я сохраняю его в состоянии компонента, чтобы новые вызовы этого компонента использовали это состояние вместо повторного вызова API. Это не производственный код, и у меня нет API, поэтому я делаю это, потому что получал сообщение «Слишком много запросов», так как я постоянно пытаюсь что-то сделать.

В любом случае, когда я пробую этот код, я получаю уже известное:

Превышена максимальная глубина обновления. Это может произойти, когда компонент многократно вызывает setState внутри componentWillUpdate или componentDidUpdate. React ограничивает количество вложенных обновлений, чтобы предотвратить бесконечные циклы.

Я изучил разметку и не думаю, что делаю какие-либо вызовы методов, которые могут вызвать повторное срабатывание метода рендеринга и так далее, поэтому я не понимаю, почему это происходит.

Спасибо заранее за любую помощь.

Вот рассматриваемый код:

 class Players extends Component {
    nbaPlayersUrl = "https://someUrl.com";
    state = {
        players: null,
        selectedTeamPlayers: null
    };

    render() {
        if (this.props.teamId === null) return null;

        if (this.state.players !== null) {
            var selectedTeamPlayers = this.filterPlayersByTeamId(this.state.players);
            var markup = this.getMarkup(selectedTeamPlayers);
            this.setState({selectedTeamPlayers: markup});
        } else {
            this.getPlayersList();
        }

        return (
            this.state.selectedTeamPlayers
        );
    }

    getPlayersList() {
        let api = new ExternalApi();
        let that = this;

        api.get(this.nbaPlayersUrl).then(r => {
            r.json().then(result => {
                let players = result.data.map(p => ({
                    id: p.id,
                    firstName: p.first_name,
                    lastName: p.last_name,
                    position: p.position,
                    heightInches: p.height_inches,
                    heightFeet: p.height_feet,
                    weightPounds: p.weight_pounds,
                    teamId: p.team.id
                }));

                that.setState({players: players});
                var selectedTeamPlayers = that.filterPlayersByTeamId(players);
                var markup = that.getMarkup(selectedTeamPlayers);
                that.setState({selectedTeamPlayers: markup});
            });
        });
    }

    filterPlayersByTeamId(players) {
        return players.filter(p => {
            return p.teamId === this.props.teamId;
        });
    }

    getMarkup(players) {
        var result = players.map(p => {
            <li key={p.id}>
                <div>
                    <label htmlFor="firstName">First Name</label> <input type="text" name="firstName" value={p.firstName} readOnly></input>
                </div>
                <div>
                    <label htmlFor="lastName">Last Name</label> <input type="text" name="lastName" value={p.lastName} readOnly></input>
                </div>
                <div>
                    <label htmlFor="position">Position</label> <input type="text" name="position" value={p.position} readOnly></input>
                </div>
            </li>
        });

        return (
            <ul>
                {result}
            </ul>
        );
    }
}

export default Players;
0
Sergio Romero 11 Фев 2021 в 23:21

3 ответа

Лучший ответ

@Sergio Romero - вы НЕ МОЖЕТЕ установить состояние в функции рендеринга, так как это установленное состояние вызовет новый рендер, который снова установит состояние и вызовет новый рендеринг, а также создаст бесконечный цикл. Ваша загрузка данных находится в состоянии рендеринга и настройки, что также создает бесконечные циклы. Вам нужно полностью переписать свой рендер, чтобы он отображал только состояние и свойства (он никогда не должен манипулировать или загружать данные). Я думаю, что вы хотите, это скорее так:

class Players extends Component {
    nbaPlayersUrl = "https://someUrl.com";
    static propTypes = {
        teamId: PropTypes.number
    };
    static defaultProps = {
        teamId: null
    };
    constructor(props) {
        super(props);
        this.state = {
            players: null
        };
    }
    componentDidMount() {
        this.getPlayerList();
    }
    filterPlayersByTeamId(players, teamId) {
        return players.filter(p => {
            return p.teamId === teamId;
        });
    }
    getPlayersList = () => {
        let api = new ExternalApi();

        api.get(this.nbaPlayersUrl).then(r => {
            r.json().then(result => {
                let players = result.data.map(p => ({
                    id: p.id,
                    firstName: p.first_name,
                    lastName: p.last_name,
                    position: p.position,
                    heightInches: p.height_inches,
                    heightFeet: p.height_feet,
                    weightPounds: p.weight_pounds,
                    teamId: p.team.id
                }));

                this.setState({players});
            });
        });
    };
    render() {
        if (!this.props.teamId || !this.state.players) return null;

        const selectedTeamPlayers = this.filterPlayersByTeamId(this.state.players, this.props.teamId);

        return (
            <ul>
                {
                    selectedTeamPlayers.map(player => {
                        <li key={player.id}>
                            <div>
                                <label htmlFor="firstName">First Name</label><input type="text" name="firstName" value={player.firstName} readOnly></input>
                            </div>
                            <div>
                                <label htmlFor="lastName">Last Name</label><input type="text" name="lastName" value={player.lastName} readOnly></input>
                            </div>
                            <div>
                                <label htmlFor="position">Position</label><input type="text" name="position" value={player.position} readOnly></input>
                            </div>
                        </li>
                    })
                }
            </ul>
        );
    }
}

export default Players;
2
Jason Bellomy 12 Фев 2021 в 02:08

Если состояние и свойства изменяются, компонент будет повторно визуализирован. в вашей функции render ():

 if (this.state.players !== null) {
      var selectedTeamPlayers = this.filterPlayersByTeamId(this.state.players);
      var markup = this.getMarkup(selectedTeamPlayers);
      // this.setState({selectedTeamPlayers: 
 }

Попробуйте изменить закомментированную строку, каждый раз, когда игроки не обнуляют, состояние компонента обновляется, поэтому функция Render компонента будет запускаться снова.

0
Re9iNee 11 Фев 2021 в 20:35

Поскольку мы не можем установить состояние внутри функции рендеринга, потому что это вызовет побочный эффект, вы не можете вызвать getPlayersList () внутри функции рендеринга.

Решение, упомянутое @Jason Bellomy, - это правильный способ решить эту проблему с помощью вызова getPlayerList внутри componentDidMount, потому что он вызывается сразу после того, как компонент вставлен в дерево, поэтому это место для установки начальных данных для рендеринга страницы.

class Players extends Component {
    nbaPlayersUrl = "https://someUrl.com";
    state = {
        players: null,
        selectedTeamPlayers: null,
    };
    
    componentDidMount(){
     this.getPlayersList();
    }

    render() {
        if (this.props.teamId === null) return null;

        if (this.state.players !== null && this.state.selectedTeamPlayers !== null) {
             return this.getMarkup(selectedTeamPlayers);
        } else {
           return (<span> Loading ... </span>);
        }
    }

    getPlayersList() {
        let api = new ExternalApi();
        let that = this;

        api.get(this.nbaPlayersUrl).then(r => {
            r.json().then(result => {
                let players = result.data.map(p => ({
                    id: p.id,
                    firstName: p.first_name,
                    lastName: p.last_name,
                    position: p.position,
                    heightInches: p.height_inches,
                    heightFeet: p.height_feet,
                    weightPounds: p.weight_pounds,
                    teamId: p.team.id
                }));
               
                 var selectedTeamPlayers = that.filterPlayersByTeamId(players);
             
                that.setState({
                      players: players, 
                      selectedTeamPlayers: selectedTeamPlayers, 
                });
            });
        });
    }

    filterPlayersByTeamId(players) {
        return players.filter(p => {
            return p.teamId === this.props.teamId;
        });
    }

    getMarkup(players) {
        var result = players.map(p => {
            <li key={p.id}>
                <div>
                    <label htmlFor="firstName">First Name</label> <input type="text" name="firstName" value={p.firstName} readOnly></input>
                </div>
                <div>
                    <label htmlFor="lastName">Last Name</label> <input type="text" name="lastName" value={p.lastName} readOnly></input>
                </div>
                <div>
                    <label htmlFor="position">Position</label> <input type="text" name="position" value={p.position} readOnly></input>
                </div>
            </li>
        });

        return (
            <ul>
                {result}
            </ul>
        );
    }
}

export default Players;
0
niiima 11 Фев 2021 в 22:30
66162220