React useEffect не срабатывает после изменения реквизита

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

Ниже приведены обобщенные компоненты

App.js

import React from "react";
import history from "./Helpers/history";
import { Router, Switch, Route } from "react-router-dom";
import Home from "./Components/Home";
import UsersWrapper from "./Components/UsersWrapper";


const App = () => {


  return (

    <Router history={history}>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/users"><UsersWrapper isUserSearch={false}/></Route>
        <Route exact path="/users/:userName"><UsersWrapper isUserSearch={true}/></Route>
      </Swith>
    </Router>
  )
}

NavigationBar.jsx

import React, { useCallback } from "react";
import {NavLink, useHistory} from "react-router-dom";
import Container from "react-bootstrap/Container";
import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import Form from "react-bootstrap/Form";
import FormControl from "react-bootstrap/FormControl";
import InputGroup from "react-bootstrap/InputGroup";
import Button from "react-bootstrap/Button";



const NavigationBar = ({isSearchForUsers}) => {

    const history = useHistory();


    const displayUser = useCallback( e => {
        e.preventDefault();
        console.log(e.target[0].value);
        const input = e.target[0].value;
        isSearchForUsers ? history.push(`/users/${input}`) : history.push(`/repositories/${input}`);
    }, []);


    return (
        <Navbar expand="lg" className="my-4 bg-secondary justify-content-center" sticky="top">
            <Container>
                <NavLink to="/" className="navbar-brand fs-1 fs-lg-4 text-white">Github API</NavLink>
                <Navbar.Toggle/>
                <Navbar.Collapse id="user-repository-navbar" className="fs-3 mt-2 mt-lg-0 ms-lg-3">
                    <Nav>
                        <NavLink to="/users" className="nav-link">Users</NavLink>
                        <NavLink to="/repositories" className="nav-link">Repositories</NavLink>
                    </Nav>
                    <Form onSubmit={ displayUser } className="ms-sm-auto mt-3 mt-lg-0">
                        <InputGroup>
                            <FormControl placeholder="Buscar"/>
                            <Button type="submit">BUSCAR</Button>
                        </InputGroup>
                    </Form>
                </Navbar.Collapse>
            </Container>
        </Navbar>
    );
};


export default NavigationBar;

UsersWrapper.jsx

import React, { useEffect, useState } from "react";
import { useParams } from "react-router";
import { useDispatch, useSelector } from "react-redux";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import { getCurrentIndex } from "../Selectors";
import NavigationBar from "./NavigationBar";
import UserCard from "./UserCard";
import SpinnerWrapper from "./SpinnerWrapper";
import PaginationWrapper from "./PaginationWrapper";
import { consumeAPI } from "../Services/usersService";
import { urlGetUser, urlGetUsersOfAPagination } from "../Constants/links";
import { PER_PAGE } from "../Constants/general";
import { setIndex } from "../Actions/indexActions";




const UsersWrapper = ({isUserSearch}) => {

    console.log(isUserSearch);

    const dispatch = useDispatch();
    const {userName} = useParams();
    const [filteredUsers, setFilteredUsers] = useState([]);
    const [isLoading, setIsLoading] = useState(false);

    const index = useSelector(getCurrentIndex);


    useEffect(() => {
        dispatch(setIndex(1));
    }, []);
    

    useEffect(() => {
        console.log("Entering useEffect");

        setIsLoading(true);

        if( isUserSearch ) {
            const getAUser = async () => {
                try {
                    const response = await consumeAPI(urlGetUser(userName));
                    console.log(response);
                    if( response.status !== 200 ) return new Promise.reject("La API no ha devuelto la informaciòn de usuario");
                    setFilteredUsers(response.data);
                    setIsLoading(false);
                } catch(error) {
                    console.error(error);
                }
            }

            getAUser();
        } else {
            const getUsersOfAPagination = async () => {
                try {
                    const response = await consumeAPI(urlGetUsersOfAPagination( (index-1)*PER_PAGE ) || 1 );
                    console.log(response.data);
                    if( response.status !== 200 ) return new Promise.reject("La API no ha devuelto los usuarios contenidos en el ìndice de paginaciòn");
                    let specificInfoByUser = [];
                    const rawUser = response.data;
                    for( let i = 0; i<rawUser.length; i++) {

                        const getActualUsersOfAPagination = async () => {
                            try {
                                // console.log(user.url)
                                const info = await consumeAPI(rawUser[i].url);
                                if( info.status !== 200 ) console.error("La informaciòn del usuario " + info.data.id + " no pudo ser recuperada");
                                specificInfoByUser.push(info.data);
                                return;
                            } catch( error ) {
                                console.error(error);
                                throw new Error(error);
                            }
                        }

                        await getActualUsersOfAPagination();
                    }

                    setFilteredUsers(specificInfoByUser);
                    setIsLoading(false);

                } catch(error) {
                    console.error(error);
                }
            };

            getUsersOfAPagination();
        }

    }, [isUserSearch, index]);

    

    return (
        
        <Container>
            <NavigationBar isSearchForUsers={true}/>

            <Row>
                {/* En caso de existir, renderiza el usuario que ingresò el cliente en el cuadro de bùsqueda */}
                {/* Renders the user filled in the search box */}
                { !isLoading && userName && filteredUsers && <UserCard login={filteredUsers.login} avatar={filteredUsers.avatar_url} name={filteredUsers.name} company={filteredUsers.company} blog={filteredUsers.blog} location={filteredUsers.location} email={filteredUsers.email} key={filteredUsers.id} /> }

                {/* Renderiza 30 usuarios correspondientes a la paginaciòn en la que estè el cliente. Estos 30 usuarios renderizados se los trae desde la API */}
                {/* Renders 30 users corresponding to the pagination the client set. These 30 rendered users are fetched from API Github endpoint */}
                { !isLoading && !userName && filteredUsers && filteredUsers.map( user => <UserCard login={user.login} avatar={user.avatar_url} name={user.name} company={user.company} blog={user.blog} location={user.location} email={user.email} key={user.id} /> )}

                {/* Renderiza un spinner en caso de que la/s tarjeta/s no hayan sido todavìa devueltas por la API de Github */}
                {/* Renders a spinner in case the cards are being retrieved */}
                { isLoading && <SpinnerWrapper/> }
            </Row>

            {/* Renderiza la paginaciòn en caso de corresponder  */}
            {/* Renders the pagination */}
            { !userName && <PaginationWrapper /> }
        </Container>
    );
};


export default UsersWrapper;

Вначале корневая страница отображается с компонентом Home, который я не описывал здесь, потому что пока он отображает просто «Hola». введите описание изображения здесь

Затем я выполняю поиск в строке поиска, и он работает хорошо: введите описание изображения здесь. Обратите внимание, что в журналах консоли строка 21 UsersWrapper показывает значение свойства isUserSearch, которое является истинным; Кроме того, в строке 37 показано «вход в useEffect», что означает, что useEffect сработал: введите описание изображения здесь.

Наконец, я нажимаю «Пользователи» на панели навигации, и происходит взрыв. Как показано на рисунке введите описание изображения здесь, всплывает ошибка «Uncaught TypeError: filteredUsers.map не является функцией», потому что useEffect не сработал, и это означает, что переменная состояния filteredUsers продолжает содержать пользовательский объект вместо массива пользователей. это должно было произойти, если бы сработал useEffect. Снова просматривая журналы консоли, я вижу, что строка 37 UsersWrapper не запустилась из-за того, что сам useEffect не запустился. Также журналы показывают, что переменная свойства isUserSearch действительно изменилась с true на предыдущем шаге на false на текущем, поэтому я не могу понять, почему это происходит.

Какие-либо предложения? Благодарю вас!

Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
0
0
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы заметили хотя бы одну вещь: у вашего UsersWrapper есть два экземпляра, и оба не имеют общих внутренних вызовов функций, таких как useEffect?

На высоком уровне вы можете сделать

  const [isUserSearch, setIsUserSearch] = useState(false)

  return (
    <>
      <Route exact path="/users"><UsersWrapper isUserSearch={!isUserSearch}/></Route>
      <Route exact path="/users/:userName"><UsersWrapper isUserSearch={isUserSearch}/></Route>
    </>
  )

Разница в том, что состояние контролируется на родительском уровне. Иначе как два дочерних компонента могут общаться внутри React?

Если я это сделаю, что будет отвечать за изменение isUserSearch? Я мог бы создать новую переменную в хранилище Redux и отправить действие в NavigationBar в зависимости от того, была ли нажата ссылка пользователя или панель поиска на кнопке Buscar. Затем, через useSelector, наблюдать за этой новой переменной хранилища и, если она истинна, запускать setIsUserSearch(false) и наоборот, если переменная хранилища ложна. Не знаю, в этом ли смысл вашего предложения.

JavierHM 23.04.2022 18:46

Вы правы, я немного изменил код. В целом вам нужно иметь переменную для родителя, чтобы изменить ее, а затем соответствующим образом управлять дочерними элементами. React НИКОГДА не предназначен для управления детьми, вместо этого все начинается с родителя или даже с ROOT.

windmaomao 24.04.2022 19:05

Спасибо за вашу помощь... Вы помогли мне изолировать проблему, когда рассказали о двух разных случаях в исходном коде. Вот так я и добрался до окончательного решения. Надеюсь, у вас все хорошо, и еще раз спасибо!

JavierHM 25.04.2022 01:40

Пожалуйста, примите ответ или проголосуйте, если это помогло вам. Спасибо.

windmaomao 26.04.2022 04:12

Это не позволяет мне голосовать, и я думаю, это потому, что я здесь новичок. Извини!

JavierHM 26.04.2022 14:13
Ответ принят как подходящий

Я нашел другой способ решить свою проблему:

    <Router history={history}>
      <Switch>
        <Route exact path="/" component={Home} />
        {["/users", "/users/:userName"].map( path => {
          if(path === "/users") {
            return <Route key={path} exact path={path} render={() => <UsersWrapper isUserSearch={false} />} />
          }
          return <Route key={path} exact path={path} render={() => <UsersWrapper isUserSearch={true} />} />
        })}
      </Switch>
    </Router>
      

В любом случае, спасибо за вашу помощь!

Ваш ответ может быть улучшен с помощью дополнительной вспомогательной информации. Пожалуйста, редактировать добавьте дополнительную информацию, например цитаты или документацию, чтобы другие могли подтвердить правильность вашего ответа. Дополнительную информацию о том, как писать хорошие ответы, можно найти в справочном центре.

Community 25.04.2022 03:56

Другие вопросы по теме