I am encountering an issue with passing the type of vehicle as a parameter to an API in my React application. The problem arises when attempting to pass the variable "vehicle" to the API endpoint; it receives undefined instead of "carros", "motos", or "caminhoes".
Here is the relevant code from my project:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { Brand } from '../pages/VehicleBrand'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: 'https://parallelum.com.br/fipe/api/v1',
}),
endpoints: (builder) => ({
getBrand: builder.query<Brand[], string>({
query: (vehicle) => `${vehicle}/marcas`,
}),
}),
})
export const { useGetBrandQuery } = api
export default api
import { PayloadAction, createSlice } from '@reduxjs/toolkit'
export type vehicleState = {
vehicle: string
}
const initialState: vehicleState = {
vehicle: 'carros',
}
const vehicleSlice = createSlice({
name: 'vehicle',
initialState,
reducers: {
change: (state, action: PayloadAction<string>) => {
state.vehicle = action.payload
},
},
})
export const { change } = vehicleSlice.actions
export default vehicleSlice.reducer
import { useParams } from 'react-router-dom'
import MainBox from '../../components/MainBox'
import { useGetBrandQuery } from '../../services/api'
export type Brand = {
codigo: string
nome: string
}
type VehicleParams = {
vehicle: string
}
const VehicleBrand = () => {
const { vehicle } = useParams() as VehicleParams
const { data: brand } = useGetBrandQuery(vehicle!)
return (
<div className="container">
<MainBox
children="Selecione a marca do veículo"
to="/VehicleModel"
selectIsVisible={true}
btnChildren="Buscar"
resultIsVisible={false}
brand={brand}
/>
</div>
)
}
export default VehicleBrand
import { configureStore } from '@reduxjs/toolkit'
import vehicleSlice from './reducers/vehicle'
import api from '../services/api'
export const store = configureStore({
reducer: {
vehicle: vehicleSlice,
[api.reducerPath]: api.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(api.middleware),
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
import { useDispatch } from 'react-redux'
import * as S from './styles'
import { change } from '../../store/reducers/vehicle'
const Header = () => {
const dispatch = useDispatch()
const handleChangeVehicle = (vehicle: string) => {
dispatch(change(vehicle))
}
return (
<S.HeaderDiv>
<S.Container className="container">
<S.Title to="/">ConsultaFipe</S.Title>
<nav>
<ul>
<S.NavLink onClick={() => handleChangeVehicle('carros')}>
Carros
</S.NavLink>
<S.NavLink onClick={() => handleChangeVehicle('motos')}>
Motos
</S.NavLink>
<S.NavLink onClick={() => handleChangeVehicle('caminhoes')}>
Caminhões
</S.NavLink>
</ul>
</nav>
</S.Container>
</S.HeaderDiv>
)
}
export default Header
I have tried debugging the issue but haven't been able to find a solution. Any insights or suggestions would be greatly appreciated.
I need to read the value of "vehicle" in the fetch in the api query. The "GET" will be done like this: "GET: https://parallelum.com.br/fipe/api/v1/carros/marcas" where the value is "car" will receive the value of vehicle.
import { Brand } from '../pages/VehicleBrand'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: 'https://parallelum.com.br/fipe/api/v1',
}),
endpoints: (builder) => ({
getBrand: builder.query<Brand[], string>({
query: (vehicle) => `${vehicle}/marcas`,
}),
}),
})
export const { useGetBrandQuery } = api
export default api
The console return this error: VM8:6 GET https://parallelum.com.br/fipe/api/v1/undefined/marcas 400 (Bad Request)
This is the routes.tsx file:
import { Routes, Route } from 'react-router-dom'
import Home from './pages/Home'
import VehicleModel from './pages/VehicleModel'
import VehicleYear from './pages/VehicleYear'
import VehicleBrand from './pages/VehicleBrand'
import VehicleInfo from './pages/VehicleInfo'
const PagesRouter = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/VehicleBrand" element={<VehicleBrand />} />
<Route path="/VehicleModel" element={<VehicleModel />} />
<Route path="/VehicleYear" element={<VehicleYear />} />
<Route path="/VehicleInfo" element={<VehicleInfo />} />
</Routes>
)
}
export default PagesRouter
It appears you have navigation items that set a vehicle
value in your Redux store, e.g. state.vehicle.vehicle
while at the same time the VehicleBrand
component is expecting to read a vehicle
value from the route params.
The issue is that there aren't any routes that provide any route params and VehicleBrand
doesn't read the current vehicle
value from state.
vehicle
from route parametersUpdate the route definitions to include a vehicle
path parameter.
const PagesRouter = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/VehicleBrand/:vehicle" // <-- add path parameter
element={<VehicleBrand />}
/>
<Route path="/VehicleModel" element={<VehicleModel />} />
<Route path="/VehicleYear" element={<VehicleYear />} />
<Route path="/VehicleInfo" element={<VehicleInfo />} />
</Routes>
);
};
Update the navigation to include a vehicle value in the target path. This is just an example of passing the target path as a prop to be used as a Link
component target.
const Header = () => {
const dispatch = useDispatch();
const handleChangeVehicle = (vehicle: string) => {
dispatch(change(vehicle));
};
return (
<S.HeaderDiv>
<S.Container className="container">
<S.Title to="/">ConsultaFipe</S.Title>
<nav>
<ul>
<S.NavLink
to="/VehicleBrand/carros"
onClick={() => handleChangeVehicle('carros')}
>
Carros
</S.NavLink>
<S.NavLink
to="/VehicleBrand/motos"
onClick={() => handleChangeVehicle('motos')}
>
Motos
</S.NavLink>
<S.NavLink
to="/VehicleBrand/caminhoes"
onClick={() => handleChangeVehicle('caminhoes')}
>
Caminhões
</S.NavLink>
</ul>
</nav>
</S.Container>
</S.HeaderDiv>
);
};
Update VehicleBrand
to skip the query if vehicle
parameter value is falsey.
const VehicleBrand = () => {
const { vehicle } = useParams() as VehicleParams;
const { data: brand } = useGetBrandQuery(vehicle, { skip: !vehicle });
return (
<div className="container">
<MainBox
children="Selecione a marca do veículo"
to="/VehicleModel"
selectIsVisible={true}
btnChildren="Buscar"
resultIsVisible={false}
brand={brand}
/>
</div>
);
};
vehicle
from stateSelect the vehicle
value from state.
const VehicleBrand = () => {
const { vehicle } = useSelector((state: AppState) => state.vehicle);
const { data: brand } = useGetBrandQuery(vehicle, { skip: !vehicle });
return (
<div className="container">
<MainBox
children="Selecione a marca do veículo"
to="/VehicleModel"
selectIsVisible={true}
btnChildren="Buscar"
resultIsVisible={false}
brand={brand}
/>
</div>
);
};