I would like to implement routing in my project but I keep getting the same error message and can't figure out how to solve the problem.
I want to start with the MappedCharacters
component and assign it to the "/"
route. Here I get the following error message: "Uncaught Error: [MappedCharacters] is not a <Route>
component. All component children of <Routes>
must be a <Route>
or <React.Fragment>
".
I have also set the browser router in Index.js (so that can't be the problem). As soon as I remove the MappedCharacters
component it works.
So how could I wrap MappedCharacters
to make routing work?
App.js
import "./App.css";
import Header from "./components/Header";
/* import styled from "styled-components"; */
import Navbar from "./components/Navbar";
import { useState, useEffect } from "react";
import MappedCharacters from "./components/MappedCharacters";
import AddFavorites from "./components/AddFavorites";
import SearchBox from "./components/Searchbox";
import { Routes, Route, useNavigate } from "react-router-dom";
const URL = 'https://rickandmortyapi.com/api/character'; /* Das hier ist der API Link in einer Variablen */
function App() {
const [characters, setCharacters] = useState([]);
const [hasError, setHasError] = useState(false)
const [favorites, setFavorites] = useState([])
const [searchValue, setSearchValue] = useState([])
/* Hier folgend wird von der API gefetched */
async function fetchCharacters() {
try {
const response = await fetch(URL);
const result = await response.json()
setCharacters(result.results);
setHasError(false);
}
catch(error) {
setHasError(true);
console.error(error);
}
}
/* Ohne den useEffect klappt das fetchen und rendern der Cards nicht!!! */
useEffect(() => {
fetchCharacters();
}, []);
const addFavoriteCharacter = (character) => {
const newFavoriteList = [...favorites, character];
setFavorites(newFavoriteList);
}
return (
<div className="App">
<Header />
<SearchBox searchValue={searchValue} setSearchValue={setSearchValue} />
<Routes>
<Route path="/">
{characters.map((character) => (
<MappedCharacters
name={character.name}
gender={character.gender}
status={character.status}
species={character.species}
image={character.image}
favoriteComponent={AddFavorites}
handleFavoritesClick={addFavoriteCharacter}
/>
))}
</Route>
</Routes>
<Navbar />
</div>
)
}
export default App;
/* const CardElements = styled.li`
color: whitesmoke;
list-style: none;
`; */
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
MappedCharacters.js
import React from 'react'
const MappedCharacters = (props) => {
const FavoriteComponent = props.favoriteComponent;
return (
<div>
<li key={props.id} >
<h1>{props.name}</h1>
<p>Gender: {props.gender}</p>
<p>Status: {props.status}</p>
<p>Species: {props.species}</p>
<div>
<img src={props.image} alt="img"></img>
</div>
<FavoriteComponent />
</li>
</div>
)
}
export default MappedCharacters
CodePudding user response:
Issue
The issue is with this route:
<Route path="/">
{characters.map((character) => (
<MappedCharacters
name={character.name}
gender={character.gender}
status={character.status}
species={character.species}
image={character.image}
favoriteComponent={AddFavorites}
handleFavoritesClick={addFavoriteCharacter}
/>
))}
</Route>
As the error indicates, only other Route
or React.Fragment
components are valid children of the Route
component. Match routed content should be rendered on the Route
component's element
prop.
Solution
Move the characters
array mapping into the element
prop.
<Route
path="/"
element={(
<>
{characters.map((character) => (
<MappedCharacters
key={character.id} // <-- don't forget valid React key!
name={character.name}
gender={character.gender}
status={character.status}
species={character.species}
image={character.image}
favoriteComponent={AddFavorites}
handleFavoritesClick={addFavoriteCharacter}
/>
))}
</>
)}
/>
Or abstract the mapping into a React component and pass the characters
array as a prop.
const Characters = ({ addFavoriteCharacter, characters }) => (
<>
{characters.map((character) => (
<MappedCharacters
key={character.id} // <-- don't forget valid React key!
name={character.name}
gender={character.gender}
status={character.status}
species={character.species}
image={character.image}
favoriteComponent={AddFavorites}
handleFavoritesClick={addFavoriteCharacter}
/>
))}
</>
);
...
<Route
path="/"
element={<Characters {...{ addFavoriteCharacter, characters }} />}
/>
CodePudding user response:
You can do as MrBens says. You could also use a Fragment
:
<Route path="/">
<>{characters.map((character) => (
<MappedCharacters
name={character.name}
gender={character.gender}
status={character.status}
species={character.species}
image={character.image}
favoriteComponent={AddFavorites}
handleFavoritesClick={addFavoriteCharacter}
/>
))}</>
</Route>
CodePudding user response:
You cannot pass a list of components as children to the Route
object.
Children has to be React.ReactNode
(which can be a single react component).
So what you can do instead is pass a single wrapper component, CharactersList
that accepts your character list, and map it accordingly:
<Route path="/">
<CharactersList characters={characters}/>
</Route>
and in CharactersList.jsx create something like this:
const CharactersList = ({ characters }) => {
return (
<>
{characters.map((character) => (
<MappedCharacters
name={character.name}
gender={character.gender}
status={character.status}
species={character.species}
image={character.image}
favoriteComponent={AddFavorites}
handleFavoritesClick={addFavoriteCharacter}
/>)
)}
</>
)
}