Действия вызываются бесконечно при вызовах API в redux-реагировать - PullRequest
0 голосов
/ 18 апреля 2020

Я пытаюсь показать загрузку, пока все компоненты внутри RootContainer не завершат свои вызовы API, но мое приложение работает в бесконечном l oop вызываемого действия диспетчеризации, и приложение всегда остается в состоянии загрузки. Он совершил около 1300 звонков за 30 секунд. Вот мой код. Я не могу найти, почему приложение работает в infinte l oop.

App. js

import React, { Component } from 'react';
import RootContainer from './components/RootContainer'
import {Provider} from 'react-redux'
import store from './redux/store';

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <div className="App">
          <RootContainer/>
        </div>
      </Provider>
    )

  }
}

export default App;

RootContainer. js

import React from 'react'
import {connect} from 'react-redux'
import {mapStateToProps , mapDispatchToprops} from './mapFunctions.js'
import UserContainer from './UserContainer'
import TodoContainer from './TodoContainer'

class RootContainer extends React.Component{
  render(){
    if(this.props.userData.count===0){
      return (
        <div>
          <UserContainer/>
          <TodoContainer/>
        </div>
      );
    }else{
      return(
        <h2> Loading... </h2>
      )
    }

  }
}

export default connect(mapStateToProps,mapDispatchToprops)(RootContainer)

UserContainer. js

import React from 'react'
import {connect} from 'react-redux'
import {mapStateToProps , mapDispatchToprops} from './mapFunctions.js'

class UserContainer extends React.Component{
    constructor(props){
      super(props)
      this.state={
        userList:[]
      }
    }
    componentDidMount() {
        this.props.fetchUsers('https://jsonplaceholder.typicode.com/users').then(data =>{
          this.setState({
            userList:data
          })
        })
    }

    render(){
        console.log("count = "+this.props.userData.count);
        return (
          <div>
            <h2>User List</h2>
            <ul>
                {this.state.userList.map(item => {
                  return <li>{item.name}</li>;
                })}
            </ul>
          </div>
        )
    }
}

export default connect(mapStateToProps,mapDispatchToprops)(UserContainer)

TodoContainer. js

import {connect} from 'react-redux'
import { fetchUsers } from '../redux'
import {mapStateToProps , mapDispatchToprops} from './mapFunctions.js'

class TodoContainer extends React.Component{
    constructor(props){
      super(props)
      this.state={
        todoList:[]
      }
    }
    componentDidMount() {
        this.props.fetchUsers('https://jsonplaceholder.typicode.com/todos').then(data =>{
          this.setState({
            todoList:data
          })
        })
    }

    render(){
        return (
          <div>
            <h2>ToDo List</h2>
            <ul>
                {this.state.todoList.map(item => {
                  return <li>{item.title}</li>;
                })}
            </ul>
          </div>
        )

    }
}

export default connect(mapStateToProps,mapDispatchToprops)(TodoContainer)

UserActions. js

import { INCREMENT_COUNT ,DECREMENT_COUNT} from "./userTypes"
import axios from 'axios'

export const incrementCount = () =>{
    return {
        type:INCREMENT_COUNT
    }
}

export const decrementCount = () =>{
    return {

        type:DECREMENT_COUNT
    }
}

export const fetchUsers = (url) =>{
    console.log("fetchUsers called" + url)
    return(dispatch) => {
        dispatch(incrementCount())
        return fetch(url)
               .then(response => {
                 dispatch(decrementCount());
                 return response.data
               })
               .catch(error => {
                const errorMsg = error.message
                console.log("error = "+errorMsg)
                dispatch(decrementCount());
              })
    }
}

UserReducer. js

import  {INCREMENT_COUNT ,DECREMENT_COUNT} from "./userTypes"

const initialState = {
    count:0,
}

const reducer = (state = initialState,action) => {
    switch(action.type){
        case INCREMENT_COUNT:
            console.log("reducer called =increment")
            return{
               count: state.count+1,
            }
        case DECREMENT_COUNT:
            return{
                count:Math.max(0,state.count-1),
            }
        default: return state
    }
}

export default reducer

mapFunctions. js

import { fetchUsers } from '../redux'

export const mapStateToProps = state =>{
    return {
        userData: state.user
    }
}
export const mapDispatchToprops = dispatch =>{
    return {
        fetchUsers: (url) => dispatch(fetchUsers(url))
    }
}

rootReducer. js

import { combineReducers } from 'redux'
import userReducer from './user/userReducer'

const rootReducer = combineReducers ({
    user:userReducer
})

export default rootReducer

store. js

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import logger from 'redux-logger'
import rootReducer from './rootReducer'

const store = createStore(rootReducer,
    composeWithDevTools(applyMiddleware(thunk,logger))
)

export default store

1 Ответ

0 голосов
/ 18 апреля 2020

Но мое приложение работает в infinte l oop вызова диспетчерского действия, и приложение всегда остается в состоянии загрузки. Он совершил около 1300 звонков за 30 секунд. Вот мой код. Я не могу выяснить, почему приложение работает в infinte l oop.

Обновление: Реорганизовано redux функциональность в отдельные компоненты с новым действием для проверки если есть какие-либо вызовы API в процессе. Вот прекрасно работающая песочница .

//UserActions.js
import * as types from "./actionType";
import { beginApiCall, apiCallError } from "./apiStatusAction";
import { handleResponse, handleError } from "./apiUtils";

export function fetchUsersSuccess(users) {
  return { type: types.LOAD_USERS_SUCCESS, users };
}

export function fetchUsers(url) {
  return (dispatch) => {
    dispatch(beginApiCall());
    return getUsers(url)
      .then((users) => {
        dispatch(fetchUsersSuccess(users));
      })
      .catch((error) => {
        dispatch(apiCallError(error));
        throw error;
      });
  };
}

export function getUsers(url) {
  return fetch(url).then(handleResponse).catch(handleError);
}
//apiStatusAction.js
import * as types from "./actionType";

export function beginApiCall() {
return { type: types.BEGIN_API_CALL };
}

export function apiCallError() {
return { type: types.API_CALL_ERROR };
}

Этот редуктор проверяет, есть ли какие-либо действия с подстрокой _SUCCESS, которая еще выполняется. Если да, то initialState увеличивается на единицу, а затем уменьшается.

import * as types from "../actions/actionType";
import initialState from "./initialState";

function actionTypeEndsInSuccess(type) {
  return type.substring(type.length - 8) === "_SUCCESS";
}

export default function apiCallStatusReducer(
  state = initialState.apiCallsInProgress,
  action
) {
  if (action.type == types.BEGIN_API_CALL) {
    return state + 1;
  } else if (
    action.type === types.API_CALL_ERROR ||
    actionTypeEndsInSuccess(action.type)
  ) {
    return state - 1;
  }

  return state;
}

Таким образом вы можете использовать редуктор apiStatus для рендеринга счетчика в вашем компоненте.

//UserContainer.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchUsers } from "../redux/actions/UserActions";
import Spinner from "./Spinner";

function UserContainer() {
  const userList = useSelector((state) => state.user);
  const loading = useSelector((state) => state.apiCallsInProgress > 0);

  const dispatch = useDispatch();

  const fetchUserList = () => {
    dispatch(fetchUsers("https://jsonplaceholder.typicode.com/users")).catch(
      (error) => {
        alert("Loading users failed" + error);
      }
    );
  };

  useEffect(() => {
    fetchUserList();
  }, []);

  if (loading) {
    return <Spinner />;
  }

  return (
    <div>
      <h1>Users</h1>
      {userList.map((user) => {
        return <li key={user.id}>{user.name}</li>;
      })}
    </div>
  );
}

export default UserContainer;
...