Introducción
Una biblioteca popular para trabajar con el estado de las aplicaciones web en react-js es redux. Sin embargo, tiene una serie de desventajas como la verbosidad (incluso junto con redux-toolkit), la necesidad de seleccionar una capa adicional (redux-thunk, redux-saga, redux-observable). Existe la sensación de que, de alguna manera, todo esto es demasiado complicado y durante mucho tiempo hubo ganchos y, en particular, el gancho useContext. Así que probé otra solución.
Aplicación de prueba
« » 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
, , , .
Después de reemplazar redux-toolkit + redux-saga y context + react-query, el código me pareció mucho más fácil y obtuve más funcionalidad para trabajar con solicitudes al servidor. Sin embargo, la parte de contexto de reacción no tiene herramientas de depuración especiales y generalmente genera preocupaciones, pero resultó ser bastante pequeña y reaccionar-devtools fue suficiente para mí. En general, estoy satisfecho con la biblioteca react-query y, en general, la idea de separar el caché en una entidad separada me parece interesante. Pero aún así, esta es una aplicación muy pequeña con varias solicitudes de obtención.
Enlaces
El diseño es correcto solo para dispositivos móviles