Introdução
Uma biblioteca popular para trabalhar com o estado de aplicativos da web em react-js é redux. No entanto, ele tem uma série de desvantagens, como verbosidade (mesmo em conjunto com o redux-toolkit), a necessidade de selecionar uma camada adicional (redux-thunk, redux-saga, redux-observable). Há uma sensação de que de alguma forma isso é muito complicado e por muito tempo existiam ganchos e, em particular, o gancho useContext. Então, tentei outra solução.
Aplicação de teste
« » 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
, , , .
Depois de substituir redux-toolkit + redux-saga e context + react-query, o código parecia muito mais fácil para mim e obtive mais funcionalidades prontas para trabalhar com solicitações ao servidor. No entanto, a parte do contexto de reação não tem ferramentas especiais de depuração e geralmente levanta preocupações, mas acabou sendo bem pequena e as ferramentas de reação foram suficientes para mim. Em geral, estou satisfeito com a biblioteca de consulta de reação e, em geral, a ideia de separar o cache em uma entidade separada parece interessante para mim. Mas, ainda assim, este é um aplicativo muito pequeno com várias solicitações get.
Links
O layout está correto apenas para dispositivos móveis
Documentação de consulta do React