[英]Why is my redux app not caching an async api call in redux-thunk?
Am new to redux.我是 redux 的新手。 I am working now on an app that shows a list of soccer leagues in each country.
我现在正在开发一个应用程序,该应用程序显示每个国家/地区的足球联赛列表。 Firstly, I am fetching a countries list.
首先,我正在获取一个国家/地区列表。 Afterwards, I am using the country name to loop through all countries to get the soccer leagues.
之后,我使用国家/地区名称遍历所有国家/地区以获取足球联赛。 Not every country has a soccer league, so I get some
null
as response, which I filter out.不是每个国家都有足球联赛,所以我得到了一些
null
作为响应,我将其过滤掉。 Then I click on a league and I am redirected to a league page.然后我点击一个联赛,然后我被重定向到一个联赛页面。 Now comes the tricky part.
现在是棘手的部分。 When I click "back", I go to my main page, but the whole api call process gets fired again.
当我单击“返回”时,我会转到主页面,但是整个 api 调用过程再次被触发。 Why?
为什么? How to prevent it?
如何预防? how to only use the data, that I fetched ones, and only use it as I need to.
如何仅使用我获取的数据,并且仅在需要时使用它。
If I would guess, than the mistake is somewhere in the reducer.如果我猜的话,那么错误就在减速器的某个地方。 I try there to cache the fetched api call in an object (
data: { ...state.data, ...}
), but am not sure, if I do this correctly.我尝试在一个对象(
data: { ...state.data, ...}
)中缓存获取的 api 调用,但我不确定,如果我这样做正确。 The second place, where I could do am istake is the useEffect
.第二个地方,我可以做的就是
useEffect
。 But of course anything else is also possible.但当然其他任何事情也是可能的。
Please help!请帮忙!
Here is my code:这是我的代码:
App.js I use react-router-dom to move between the conatiners: App.js 我使用 react-router-dom 在容器之间移动:
import React from 'react';
import {Switch, Route, NavLink, Redirect} from "react-router-dom";
import SignedIn from '../signedIn/signedIn';
import SignedOut from '../signedOut/signedOut';
//Components/Containers
import AllLeagues from '../allLeagues/allLeagues/allLeagues';
import League from "../allLeagues/league/league";
const App = () => {
return (
<div className="App">
<nav>
<NavLink to={"/"}>SEARCH</NavLink>
</nav>
<Switch>
<Route path={"/"} exact component={AllLeagues} />
<Route path={"/allLeagues/:league"} exact component={League} />
<Route path={"/signedin"} exact component={SignedIn} />
<Route path={"/signedout"} exact component={SignedOut} />
<Redirect to={"/"} />
</Switch>
</div>
);
}
export default App;
Here is my Page, where I make the api calls to get the countries and the soccer leagues: allLeagues.js这是我的页面,我在那里调用 api 来获取国家和足球联赛:allLeagues.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {Link} from "react-router-dom";
import _ from "lodash";
import shortid from "shortid";
import { allLeagues } from "../../../actions/leagues/allLeagues/allLeagues";
import { allCountries } from "../../../actions/allCountries/allCountries";
//the api provides 255 country names.
const ALL_COUNTRIES_LENGTH = 254;
const AllLeagues = () => {
const dispatch = useDispatch();
const selectAllCountries = useSelector(state => state.allCountries);
const selectAllLeagues = useSelector(state => state.allLeagues);
useEffect(() => {
dispatch(allCountries());
}, [dispatch]);
useEffect(() => {
if(!_.isEmpty(selectAllCountries.data)) {
selectAllCountries.data.countries.map(el => dispatch(allLeagues(el.name_en)));
}
}, [dispatch, selectAllCountries.data]);
let allCountriesArr = [];
let allLeaguesFiltered = [];
let getAllLeagues = [];
allCountriesArr = (Object.values(selectAllLeagues.data));
console.log(Object.values(selectAllLeagues.data));
if(allCountriesArr.length > ALL_COUNTRIES_LENGTH) {
allLeaguesFiltered = allCountriesArr.flat().filter(el => el !== null);
getAllLeagues = allLeaguesFiltered.flat();
}
let getAllZeroDivisionLeagues = [];
let getAllFirstDivisionLeagues = [];
let getAllSecondDivisionLeagues = [];
let getAllThirdDivisionLeagues = [];
if(!_.isEmpty(getAllLeagues)) {
getAllZeroDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "0");
getAllFirstDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "1");
getAllSecondDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "2");
getAllThirdDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "3");
}
const showData = () => {
if(!_.isEmpty(selectAllLeagues.data)) {
return(
<div>
Most Favorited Leagues:
<br/>
{getAllZeroDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
<br/>
<br/>
First Leagues:
<br/>
{getAllFirstDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
<br/>
<br/>
Second Leagues:
<br/>
{getAllSecondDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
<br/>
<br/>
Third Leagues:
<br/>
{getAllThirdDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
</div>
)
}
if (selectAllLeagues.loading) {
return <p>loading...</p>
}
if (selectAllLeagues.errorMsg !== "") {
return <p>{selectAllLeagues.errorMsg}</p>
}
return <p>Loading...</p>;
}
return (
<div>
<br/>
<br/>
All Leagues:
<br />
<br />
{showData()}
</div>
)
}
export default AllLeagues;
The both action files: allCountries.js两个动作文件:allCountries.js
import { GET_ALL_COUNTRIES_LOADING, GET_ALL_COUNTRIES_SUCCESS, GET_ALL_COUNTRIES_FAIL } from "../index";
import theSportsDB from "../../apis/theSportsDB";
export const allCountries = () => async (dispatch) => {
try {
dispatch ({
type: GET_ALL_COUNTRIES_LOADING
})
const response = await theSportsDB.get("all_countries.php");
dispatch ({
type: GET_ALL_COUNTRIES_SUCCESS,
payload: response.data
})
} catch (e) {
dispatch ({
type: GET_ALL_COUNTRIES_FAIL
})
}
}
and allCountriesReducer:和 allCountriesReducer:
import {GET_ALL_COUNTRIES_LOADING, GET_ALL_COUNTRIES_SUCCESS, GET_ALL_COUNTRIES_FAIL} from "../../actions/index";
const DefaultState = {
loading: false,
data: [],
errorMsg: ""
};
const AllCountriesReducer = (state = DefaultState, action) => {
switch (action.type){
case GET_ALL_COUNTRIES_LOADING:
return {
...state,
loading: true,
errorMsg: ""
};
case GET_ALL_COUNTRIES_SUCCESS:
return {
...state,
loading: false,
data: {
...state.data,
countries: action.payload.countries
},
errorMsg: ""
};
case GET_ALL_COUNTRIES_FAIL:
return {
...state,
loading: false,
errorMsg: "unable to get all the Countries"
};
default:
return state;
}
}
export default AllCountriesReducer;
Now the files, with which I fetch the all the leagues (with the country name, that I got from allCountries):现在文件,我用它获取所有联赛(带有国家名称,我从所有国家获得):
import { GET_ALL_LEAGUES_LOADING, GET_ALL_LEAGUES_SUCCESS, GET_ALL_LEAGUES_FAIL } from "../../index";
import theSportsDB from "../../../apis/theSportsDB";
export const allLeagues = (country) => async (dispatch) => {
try {
dispatch ({
type: GET_ALL_LEAGUES_LOADING
})
const response = await theSportsDB.get(`search_all_leagues.php?c=${country}&s=Soccer`);
dispatch ({
type: GET_ALL_LEAGUES_SUCCESS,
payload: response.data,
countryName: country
})
} catch (e) {
dispatch ({
type: GET_ALL_LEAGUES_FAIL
})
}
}
and the reducer, allLeaguesReducer.js和减速器 allLeaguesReducer.js
import {GET_ALL_LEAGUES_LOADING, GET_ALL_LEAGUES_SUCCESS, GET_ALL_LEAGUES_FAIL} from "../../../actions/index";
const DefaultState = {
loading: false,
data: {},
errorMsg: ""
};
const AllLeaguesReducer = (state = DefaultState, action) => {
switch (action.type){
case GET_ALL_LEAGUES_LOADING:
return {
...state,
loading: true,
errorMsg: ""
};
case GET_ALL_LEAGUES_SUCCESS:
return {
...state,
loading: false,
data:{
...state.data,
[action.countryName]: action.payload.countrys
},
errorMsg: ""
};
case GET_ALL_LEAGUES_FAIL:
return {
...state,
loading: false,
errorMsg: "unable to get all the leagues"
};
default:
return state;
}
}
export default AllLeaguesReducer;
Also the leagues page itself:还有联赛页面本身:
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {Link} from "react-router-dom";
import _ from "lodash";
import shortid from "shortid";
import { getLeague } from "../../../actions/leagues/league/getLeague";
const League = (props) => {
const leagueName = props.match.params.league;
const dispatch = useDispatch();
const selectLeague = useSelector(state => state.league);
useEffect (() => {
dispatch(getLeague(leagueName));
}, [dispatch, leagueName]);
const showLeague = () => {
if(!_.isEmpty(selectLeague.data)) {
return selectLeague.data.teams.map(el => {
return (
<div key={shortid.generate()}>
{el.strTeam}
</div>
)
})
}
if(selectLeague.loading) {
return <p>loading...</p>
}
if(selectLeague.errorMsg !== "") {
return <p>{selectLeague.errorMsg}</p>
}
return <p>Unable to get the league data</p>
}
return (
<div>
<p>{leagueName}</p>
{showLeague()}
<Link to={"/"}>Back</Link>
</div>
)
}
export default League;
its action file:它的动作文件:
import { GET_LEAGUE_LOADING, GET_LEAGUE_SUCCESS, GET_LEAGUE_FAIL } from "../../index";
import theSportsDB from "../../../apis/theSportsDB";
export const getLeague = (league) => async (dispatch) => {
try {
dispatch ({
type: GET_LEAGUE_LOADING
})
const response = await theSportsDB.get(`search_all_teams.php?l=${league}`);
dispatch ({
type: GET_LEAGUE_SUCCESS,
payload: response.data,
// leagueName: league
})
} catch (e) {
dispatch ({
type: GET_LEAGUE_FAIL
})
}
}
and the reducer:和减速机:
import { GET_LEAGUE_LOADING, GET_LEAGUE_SUCCESS, GET_LEAGUE_FAIL } from "../../../actions/index";
const DefaultState = {
loading: false,
data: {},
errorMsg: ""
};
const LeagueReducer = (state = DefaultState, action) => {
switch (action.type) {
case GET_LEAGUE_LOADING:
return {
...state,
loading: true,
errorMsg: ""
};
case GET_LEAGUE_SUCCESS:
return {
...state,
loading: false,
data: action.payload,
errorMsg: ""
};
case GET_LEAGUE_FAIL:
return {
...state,
loading: false,
errorMsg: "league not found"
};
default:
return state
}
}
export default LeagueReducer;
In Redux dev Tools, when I press on back, to get again to my home page, the following is triggered (in status bar): GET_ALL_COUNTRIES_LOADING and after some time: GET_ALL_LEAGUES_SUCCESS again.在 Redux 开发工具中,当我按下返回键以再次返回我的主页时,会触发以下内容(在状态栏中):GET_ALL_COUNTRIES_LOADING 一段时间后:再次 GET_ALL_LEAGUES_SUCCESS。 So it is making an api call again.
所以它再次进行api调用。
You need to use a conditional in useEffect
so that it doesn't run each time you load the page.您需要在
useEffect
使用条件,以便它不会在您每次加载页面时都运行。
Try this:尝试这个:
useEffect(() => {
if (selectAllCountries.data.length < 1) {
disptch(getCountries());
}
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.