Здесь абсолютно не нужен useState для url, потому как url зависит только от navigation.pathname и таким образом это просто вычисляемое значение. Из-за useState твой url "отстаёт на один рендер" от запроса данных - то есть на момент срабатывания useEffect с fetch значение url всегда "films". Потом ставится исправленный url, но fetch заново не вызывается - у него зависимости [].
Далее, надо учесть кейс, когда pathname может поменяться. Тогда надо перевызвать useEffect с fetch. Заодно есть смысл размещать данные в отдельных ячейках - это может предотвратить состояние гонки, плюс это будет легче типизировать.
Итого, упрощенно:
const {data, setData} = useState({});
const { pathname } = useLocation();
const apiPath = pathname === "/films" ? "films" :
pathname === "/starships" ? "starships" :
pathname === "/people" ? "people" :
pathname === "/planets" ? "planets" : "";
useEffect(() => {
const url = `https://swapi.dev/api/${apiPath}`;
fetch(url)
.then((res) => res.json())
.then((resp) => {
setData((data) => ({
...data,
[apiPath]: resp.results,
}));
});
}, [apiPath]);
.......
<Route path={"/films"} element={<Films value={data["films"]} />} />
.....
ну а внутри Films (и прочих) проверять что если value == null, то данные пока не загрузились, надо подождать.
можно ещё ошибку загрузки обработать, тут сам справишься.