Introduction
A popular library for working with the state of web applications in react-js is redux. However, it has a number of disadvantages such as verbosity (even in conjunction with redux-toolkit), the need to select an additional layer (redux-thunk, redux-saga, redux-observable). There is a feeling that somehow this is all too complicated and for a long time there were hooks and in particular the useContext hook .. So I tried another solution.
Test application
Β« Β» create react app, typescript, redux-toolkit, redux saga. redux context + react-query. , , , react-query . .. , .. , . .. .
react-query , , .. redux 2 . β , . β , .
react-context. :
export const CitiesProvider = ({
children,
}: {
children: React.ReactNode;
}): JSX.Element => {
const [citiesState, setCitiesState] = useLocalStorage<CitiesState>(
'citiesState',
citiesStateInitValue,
);
const addCity = (id: number) => {
if (citiesState.citiesList.includes(id)) {
return;
}
setCitiesState(
(state: CitiesState): CitiesState => ({
...state,
citiesList: [...citiesState.citiesList, id],
}),
);
};
// removeCity.., setCurrentCity..
return (
<itiesContext.Provider
value={{
currentCity: citiesState.currentCity,
cities: citiesState.citiesList,
addCity,
removeCity,
setCurrentCity,
}}
>
{children}
</itiesContext.Provider>
);
};
, setCurrentCity, removeCity
. , localStorage . , , , , .
React-query
, , react-query. :
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import { CitiesProvider } from './store/cities/cities-provider';
const queryClient = new QueryClient();
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<CitiesProvider>
<App />
:
const queryCities = useQuery('cities', fetchCitiesFunc);
const cities = queryCities.data || [];
'cities'
, . - , Promise, . .
useQuery UseQueryResult
, ,
const { isLoading, isIdle, isError, data, error } = useQuery(..
export function useCurrentWeather(): WeatherCache {
const { currentCity } = useContext(itiesContext);
//
const queryCities = useQuery('cities', fetchCitiesFunc, {
refetchOnWindowFocus: false,
staleTime: 1000 * 60 * 1000,
});
const citiesRu = queryCities.data || [];
// ..
const city = citiesRu.find((city) => {
if (city === undefined) return false;
const { id: elId } = city;
if (currentCity === elId) return true;
return false;
});
const { id: weatherId } = city ?? {};
//
const queryWeatherCity = useQuery(
['weatherCity', weatherId],
() => fetchWeatherCityApi(weatherId as number),
{
enabled: !!weatherId,
staleTime: 5 * 60 * 1000,
},
);
const { coord } = queryWeatherCity.data ?? {};
// .
const queryForecastCity = useQuery(
['forecastCity', coord],
() => fetchForecastCityApi(coord as Coord),
{
enabled: !!coord,
staleTime: 5 * 60 * 1000,
},
);
return {
city,
queryWeatherCity,
queryForecastCity,
};
}
staleTime
β , , . , . , staleTime =0
.
enabled: !!weatherId
, . useQuery
isIdle
. .
const queryWeatherCity = useQuery(['weatherCity', weatherId],..
, , + .
:
export function Forecast(): React.ReactElement {
const {
queryForecastCity: { isFetching, isLoading, isIdle, data: forecast },
} = useCurrentWeather();
if (isIdle) return <LoadingInfo text=" " />;
if (isLoading) return <LoadingInfo text=" " />;
const { daily = [], alerts = [], hourly = [] } = forecast ?? {};
const dailyForecastNext = daily.slice(1) || [];
return (
<>
<Alerts alerts={alerts} />
<HourlyForecast hourlyForecast={hourly} />
<DailyForecast dailyForecast={dailyForecastNext} />
{isFetching && <LoadingInfo text=" " />}
</>
);
}
isLoading β isFetching - .
React-query . Redux, ( )
, Actions, , , .. , , , . . :
import { ReactQueryDevtools } from 'react-query/devtools';
, process.env.NODE_ENV === 'production'
, . Create React App .
react-query , , , .
useQueries
. ..useQuery
.
const userQueries = useQueries(
users.map(user => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
}
})
, , 3 .
retry
.
, ,
useMutations
const mutation = useMutation(newTodo => axios.post('/todos', newTodo))
,
useInfiniteQuery
, , , .
After replacing redux-toolkit + redux-saga and context + react-query, the code seemed much easier to me and I got more functionality out of the box for working with requests to the server. However, the react-context part does not have special debugging tools and generally raises concerns, but it turned out to be quite small and react-devtools was enough for me. In general, I am satisfied with the react-query library and, in general, the idea of ββseparating the cache into a separate entity seems interesting to me. But still this is a very small application with several get requests ..
Links
Layout is correct only for mobile devices