Ошибка: превышена максимальная глубина обновления. Это может произойти, когда компонент многократно вызывает ... бесконечное l oop при попытке setState - PullRequest
2 голосов
/ 12 июля 2020

Новичок в кодировании, я пытаюсь создать панель поиска для карты Google, где ввод запроса (в Search. js) приведет к тому, что маркеры будут отфильтрованы в соответствии с тем, что введено (в Map. js).

Я получаю обратно отфильтрованный массив (filterList) нормально, но когда я пытаюсь установить его в любое состояние (place или newPlaces), он возвращается с бесконечным l oop.

Заранее благодарим.

Приложение. js

import Map from './components/map/Map';
import Navbar from './components/Navbar';

class App extends Component {

  constructor(props) {
    super(props);

      this.state = {
        letters: '',
        newPlaces: '',
        places: [
          {
            name: "PL",
            lat: 51.510228,
            lng: -0.132992,
            type: "alive"
          },
          ...
         ]
       }

      this.searchState=this.searchState.bind(this)
  };

  // set the state of letters to match the Search.js input field
  searchState = (e) => {
    this.setState({letters: e}, this.handleSubmit)
  }

  // compare the filtered array with the original array in state and kick out the members of state.places that are missing in the filtered array
  searchAdjustedArray = () => {
    const filteredList = this.state.places.filter(club =>
      club.name.toUpperCase().includes(this.state.letters.toUpperCase())
    );

  // here, is where either the places array must be ammended and pasted into Map as prop, or a new array - newPlaces, must be ammended, and passed as props into map.. infinit loops
    this.setState({ newPlaces: filteredList })

  }

  render() {

    return (
      <div>
        <Navbar
          childMethodToChangeState={this.searchState}
        />
        {this.searchAdjustedArray()}
        <Map
          markers={this.state.newPlaces}
        />
      </div>
    );
  }
}

export default App;

где поле ввода является компонентом панели навигации. js

import Search from '../components/Search';

const Navbar = ({ childMethodToChangeState }) => {

        return (
            <nav className="d-flex navbar-expand-lg navbar-light bg-light">
                <a className="navbar-brand" href="www.sassyladancer.com">Sassy's</a>
                <button className="navbar-toggler" 
                        type="button"
                        data-toggle="collapse"
                        data-target="#navbarSupportedContent"
                        aria-controls="navbarSupportedContent"
                        aria-expanded="false"
                        aria-label="Toggle Navigation">
                    <span className="navbar-toggler-icon"></span>
                </button>

                <div className="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul className="navbar-nav mr-auto">
                        <li className="nav-item">
                            <a className="nav-link" href="#">Reviews</a>
                        </li>
                        <li className="nav-item">
                            <a className="nav-link" href="#">Testimonials</a>
                        </li>
                        <li className="nav-item">
                            <a className="nav-link" href="#">Special Offers</a>
                        </li>
                    </ul>
                    <form className="form-inline my-2 my-lg-0">
                        <Search 
                            childMethodToChangeState={childMethodToChangeState}
                        />
                        <button className="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
                    </form>
                </div>
            </nav>
        );
    }


export default Navbar;

Поиск . js




const Search = ({ childMethodToChangeState }) => {
    const [text, setText] = useState('')

    const searchChange = (q) => {
        setText(q)
        childMethodToChangeState(q)
    }

    return (
        <section className='search'>
            <form>
                <input
                    type='text'
                    className='form-control'
                    placeholders='Search Clubs'
                    value={text}
                    onChange={(e) => searchChange(e.target.value)}
                    autoFocus
                />
            </form>
        </section>
    )
}

export default Search;

Карта. js

import React, { Component } from 'react';
import { compose, withProps } from 'recompose';
import { 
    withScriptjs, 
    withGoogleMap, 
    GoogleMap,
    Marker, 
} from 'react-google-maps';

import icon1 from '../map/icon1.svg';
import iconDead from '../map/iconDead.svg';

const API = 'AIzaSyD_INqSpFnl3D3qLAq-jomYcYkztdUbCk0';

const MapWithMarkers = compose(
    withProps({
        googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${API}&callbak=initMap`,
        loadingElement: <div style={{ height: `100%` }} />,
        containerElement: <div style={{ height: `800px` }} />,
        mapElement: <div style={{ height: `100%` }} />,
    }),
    withScriptjs,
    withGoogleMap,
)((props) =>
        <GoogleMap
            center={{ lat: 51.510228, lng: -0.132992 }}
            zoom={10}
            defaultOptions={{
                styles: mapStyles
            }}
        >
            {props.markers.map(place => {
                return (
                    <MarkerWithSearch
                        key={place.id}
                        position={{ lat: place.lat, lng: place.lng }} 
                        content={place.name} 
                        icon={place.type}                  
                    />
                );
            })}
        </GoogleMap>
);


class MarkerWithSearch extends Component {

    constructor() {
        super();
        this.state = {     
            isOpen: false
        }
        this.onToggleOpen = this.onToggleOpen.bind(this);
    }

    onToggleOpen() {
        this.setState({
            isOpen: !this.state.isOpen
        });
    }

    render() {
        
        return (
            <Marker
                id={this.props.id}
                position={this.props.position}
                onClick={this.onToggleOpen}
                icon={ this.props.icon === "alive" ? 
                ({
                    url: icon1,
                    scaledSize: new window.google.maps.Size(45, 45)
                }) :
                ({
                    url: iconDead,
                    scaledSize: new window.google.maps.Size(45, 45)
                })}
                >
            </Marker>
        )
    }
}

Ответы [ 3 ]

2 голосов
/ 12 июля 2020

Когда вы вызываете setstate, он запускает рендеринг. В вашем приложении js вы вызываете setstate при каждом рендеринге, поэтому у вас есть бесконечность l oop. Вы можете изменить свой код, набрав

import Map from './components/map/Map';
import Navbar from './components/Navbar';

class App extends Component {

  constructor(props) {
    super(props);

      this.state = {
        letters: '',
        newPlaces: '',
        places: [
          {
            name: "PL",
            lat: 51.510228,
            lng: -0.132992,
            type: "alive"
          },
          ...
         ]
       }

      this.searchState=this.searchState.bind(this)
  };

  // set the state of letters to match the Search.js input field
  searchState = (e) => {
    this.setState({letters: e}, this.handleSubmit)
  }

  // compare the filtered array with the original array in state and kick out the members of state.places that are missing in the filtered array
  searchAdjustedArray = () => {
    const filteredList = this.state.places.filter(club =>
      club.name.toUpperCase().includes(this.state.letters.toUpperCase())
    );

  // here, is where either the places array must be ammended and pasted into Map as prop, or a new array - newPlaces, must be ammended, and passed as props into map.. infinit loops
    this.setState({ newPlaces: filteredList })

  }

  render() {
   useEffect( this.searchAdjustedArray,
  [this.state.places, this.state.letters]
);
    return (
      <div>
        <Navbar
          childMethodToChangeState={this.searchState}
        />
        <Map
          markers={this.state.newPlaces}
        />
      </div>
    );
  }
}

export default App;
1 голос
/ 12 июля 2020
{this.searchAdjustedArray()}

Проблема здесь. вы вызываете функцию в блоке рендеринга. Но внутри функции вы вызываете this.setState. В React вызов setState повторно отображает компонент, что означает, что ваша функция render будет вызвана снова. поэтому this.searchAdjustedArray() вызывается снова (бесконечно)

0 голосов
/ 12 июля 2020

Я закончил тем, что вставил searchAdjustedArray в searchState, и, похоже, все работает! : D

  searchState = (e) => {
    this.setState({letters: e})
    this.searchAdjustedArray();
  }

  // compare the filtered array with the original array in state and kick out the members of state.places that are missing in the filtered array
  searchAdjustedArray = () => {
    const filteredList = this.state.places.filter(club =>
      club.name.toUpperCase().includes(this.state.letters.toUpperCase())
    );
    this.setState ({ newPlaces: filteredList })
  }
...