having issues to parse array of objects in React.js - javascript

I've got api response from mongodb
res
res
Code:
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import style from './CarsListPage.module.scss';
import cars from './car-content';
import CarsList from '../components/CarsList';
const CarsListPage = () => {
const [carsInfo, setCarsInfo] = useState({ name: 'Name', year: 2000 });
useEffect(() => {
const loadCarsInfo = async () => {
const response = await axios.get('/api/cars');
const newCarsInfo = response.data;
setCarsInfo(newCarsInfo);
//console.log(newCarsInfo[1].name);
};
loadCarsInfo();
}, []);
//console.log(carsInfo);
return (
<div className={style.mainCotainer}>
{/* {carsInfo.forEach((car) => {
console.log(car.name);
})} */}
<main className={style.main}>
{console.log(carsInfo)}
<h1>Cars</h1>
<div className={style.container}>
{/* <CarsList cars={cars} style={style} /> */}
{cars.map((car) => (
<Link to={`/cars/${car.name}`} key={car.id}>
{carsInfo.forEach((car) => {
console.log(car.name);
})}
<div className={style.card}>
<h3>{car.title}</h3>
<p>{car.body_type}</p>
<p>{car.origin}</p>
</div>
</Link>
))}
</div>
</main>
</div>
);
};
export default CarsListPage;
p.s. if you need more code or info feel free to ask anything
I tried:
carsInfo.forEach((car) => {
console.log(car.name);
})
but i keep getting => 'carsInfo.forEach is not a function' issue
BUT
I need to make something like this:
{cars.map((car) => (
<Link to={`/cars/${car.name}`} key={car.id}>
{carsInfo.forEach((car) => {
console.log(car.name);
})}
<div className={style.card}>
<h3>{car.title}</h3>
<p>{car.body_type}</p>
<p>{car.origin}</p>
</div>
</Link>
))}
The code above is working but it is not API respnse it is just some data that i stored within my project as json file

Your initial state contains/is an object:
{ name: 'Name', year: 2000 }
You cannot use array methods (such as forEach) on objects. I assume you wanted to have an array/list of cars?
In that case, you need to initialize your state with an array containing your initial car object:
[{ name: 'Name', year: 2000 }]
Now, you can do forEach or map or any other array method on it, even before you get results from your API endpoint.

Related

React-Router - How to show data passed from one component to another using useNavigate or Link and useLocation

I'm basically trying to show some data in one of my components. The data is passed from my main page but I can't get it to work using useLocation.
I'm getting the data from my firebase db.
My main page is a job board and I want users to be able to click on the job card and go to a new page with all the details of that job.
I see on the console that I get the data but I can't seem to display it in my component/page. I get undefined when doing console.log
See below for more details:
Jobs.js
import { Link, useNavigate } from "react-router-dom";
export default () => {
const navigate = useNavigate();
const [jobs, setJobs] = useState([]);
return (
<div>
{jobs.map(job => {
return (
<div key={job.id}>
//My attempt using Link
<Link
to={`/view-contact-details/${job.id}`}
state={{jobs}}
>
<button>View</button>
</Link>
//My attempt using useNavigate
<button onClick={() => {
navigate(`/view-contact-details/${job.id}`, { state:{ jobs } });
}}
>
Go To Job details
</button>
</div>
);
})}
</div>
);
};
App.js
import React from "react";
import Jobs from "./pages/jobs"
import Form from "./pages/form"
import { Routes, Route } from "react-router-dom"
import ViewUserDetails from "./components/Job/ViewJobDetails";
export default () => {
return (
<div className="App">
<Routes>
<Route exact path='/' element={<Jobs/>} />
<Route exact path='/form' element={<Form/>} />
<Route
exact
path="/view-contact-details/:id"
element={<ViewJobDetails/>}
/>
</Routes>
</div>
);
};
ViewJobDetails.js
import React from "react";
import { useLocation, } from "react-router-dom";
export default (props) => {
const location = useLocation();
console.log(location); // shows jobs on the page yet can't display
//I also tried
//const {state} = location
//{state.job.description}
return (
<>
<div>
<div>
<div>
<strong>Description:</strong>
{location.state.job.description} //doesn't work
{location.job.description} //doesn't work
{location.state.description} //doesn't work
</div>
<div>
<strong>Title:</strong>
</div>
</div>
</div>
</>
);
};
console.log output
The passed state jobs is an array. In the component you are accessing a job property that is undefined.
Access the correct state.jobs array and map it to JSX.
Example:
export default (props) => {
const { state } = useLocation();
const { jobs } = state || []; // <-- access state.jobs
return (
<div>
{jobs.map(job => ( // <-- map jobs array
<React.Fragment key={job.id}>
<div>
<strong>Description:</strong>
{job.description}
</div>
<div>
<strong>Title:</strong>
{job.title}
</div>
</React.Fragment>
))}
</div>
);
};
If on the offhand chance you meant to pass only a single job, i.e. the current job from Jobs, then instead of passing the entire array, pass only the currently iterated job object.
export default () => {
const navigate = useNavigate();
const [jobs, setJobs] = useState([]);
return (
<div>
{jobs.map(job => {
return (
<div key={job.id}>
//My attempt using Link
<Link
to={`/view-contact-details/${job.id}`}
state={{ job }} // <-- pass current job
>
<button>View</button>
</Link>
//My attempt using useNavigate
<button
onClick={() => {
navigate(
`/view-contact-details/${job.id}`,
{ state:{ job } } // <-- pass current job
);
}}
>
Go To Job details
</button>
</div>
);
})}
</div>
);
};
Then in the JobDetals component access location.state.job.
export default (props) => {
const { state } = useLocation();
const { job } = state || {};
return (
<>
<div>
<div>
<div>
<strong>Description:</strong>
{job.description}
</div>
<div>
<strong>Title:</strong>
{job.title}
</div>
</div>
</div>
</>
);
};

How can I reload a react js component after filtering data

I will appreciate your help with my project.
I created button filters for a component that displays list of courses in a nextjs project. When I click on a button it filters the courses but when I refresh the page it gives me the error below:
./pages/index.js
Module parse failed: Identifier 'courses1' has already been declared (15:15)
File was processed with these loaders:
./node_modules/next/dist/build/webpack/loaders/next-swc-loader.js
You may need an additional loader to handle the result of these loaders.
Please see code below.
import { useState, useEffect } from "react"
import axios from "axios"
import CourseCard from "../components/cards/CourseCard";
import { Button } from 'antd'
const Index = ({ courses }) => {
const allTopics = ['All', ...new Set(courses.map(courses => courses.topic))]
const [courses, setCourses] = useState(courses)
const [buttons, setButtons] = useState(allTopics)
const filter = (button) => {
if (button === 'All') {
setCourses(courses)
}
const filteredData = courses.filter(courses => courses.topic === button)
setCourses(filteredData)
}
return (
<>
<h1 className="jumbotron p-5 text-center bg-primary text-white square">OEP</h1>
<div className="container-fluid">
<div>
{
buttons.map((topic, i) => {
return <Button onClick={() => filter(topic)} className="btn ms-2">{topic}</Button>
})
}
</div>
<div className="row">
{courses.map((course) => <div key={course._id} className="col-md-4">
<CourseCard course={course} />
</div>)}
</div>
</div>
</>
)
};
export async function getServerSideProps() {
const { data } = await axios.get(`${process.env.API}/courses`);
return {
props: {
courses: data,
},
}
}
export default Index;
The constant courses you declared (in line 15) already existed. It was destructured from the parameters of Index. Try to change the name of one of these 2 variables.

useCallback is not working when using with child component of same type

So I have a parent Component and a child component. And I use the child component twice in my parent component. I pass them two different state values as props and two different events as props. I have tried to memoize both the callbacks , but both the child are re-rendered even if one child callback is triggred. Why is useCallback not working.
Parent Component:
import { useState, useCallback, useEffect, useMemo } from 'react';
import './App.css'
import List from "./components/list";
import LocalList from "./components/localList";
function App() {
const itemsToBuy = [
'Baby Shoes',
'Grinder',
'Car'
]
const [buyList, updateBuyList] = useState(itemsToBuy);
const [sellList, updateSellList] = useState([
'Bed',
'Sofa'
]);
/** code to check the re-rendering of the componnet */
useEffect(() => {
console.log(`parent is being rendered`)
})
/**trying to update the state from internal method to be passed as props */
const updateBuyClick = useCallback(val => {
updateBuyList(prev => [...prev, val])
}, [buyList])
const updateSellClick = useCallback(val => {
console.log('memo of sell is called')
updateSellList(prev => [...prev, val])
}, [sellList])
return (
<>
<div className='container'>
<div>
<h1>Items To Buy</h1>
<List itemsArray={buyList} onUpdateClick={updateBuyClick} buttonText='Add Items to Buy' idx={'list One'}></List>
</div>
<div>
<h1>Items to Sell</h1>
<List itemsArray={sellList} onUpdateClick={updateSellClick} buttonText='Add Items to Sell' idx={'list Two '}></List>
</div>
{/* <div>
<h1>List that is not re-rendere</h1>
<LocalList buttonText='Add Items to LocalList' idx={'list3 '}></LocalList>
</div> */}
</div>
</>
);
}
export default App;
Child Component:
import { useState , useEffect} from "react";
import './list.css'
function List({ itemsArray = [], buttonText, onUpdateClick, idx }) {
let currentSell = '';
useEffect(() => {
console.log(`${idx} is being rendered`)
})
const updateCurrentSell = (val) => {
currentSell = val;
}
return (
<>
<ul>
{itemsArray.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
<div>
<input type='text' onChange={(e) => { updateCurrentSell(e.target.value) }}></input>
<button onClick={() => { onUpdateClick(currentSell) }}>{buttonText}</button>
</div>
</>
)
}
export default List;
There are two reasons that's not working:
You're telling useCallback to throw away the stored copy of your function when the buyList or sellList changes by including those in your dependencies array. You don't need those dependencies, because you're (correctly) using the callback version of the state setters. So you aren't using buyList or sellList in the callbacks. Just remove them from the arrays.
const updateBuyClick = useCallback(val => {
updateBuyList(prev => [...prev, val])
}, [])
// ^^−−− empty
const updateSellClick = useCallback(val => {
console.log('memo of sell is called')
updateSellList(prev => [...prev, val])
}, [])
// ^^−−− empty
useCallback only does half the necessary work: making sure the functions don't change unnecessarily. But your List component has to do the other half of the work: not re-rendering if its props don't change. With a function component, you do that with React.memo:
const List = React.memo(function List({ itemsArray = [], buttonText, onUpdateClick, idx }) {
// ...
});
React.memo memoizes the component and reuses its last rendering if its props don't change. (You can customize that by providing a callback as its second argument, see the documentation for details.)
Between those two changes, you'll see only the appropriate instances of List re-render when things change.
Live Example:
const { useState, useCallback, useEffect, useMemo } = React;
function App() {
const itemsToBuy = [
"Baby Shoes",
"Grinder",
"Car"
];
const [buyList, updateBuyList] = useState(itemsToBuy);
const [sellList, updateSellList] = useState([
"Bed",
"Sofa"
]);
// *** Note: No need for this to be in `useEffect`
console.log(`parent is being rendered`)
const updateBuyClick = useCallback(val => {
updateBuyList(prev => [...prev, val]);
}, []);
const updateSellClick = useCallback(val => {
updateSellList(prev => [...prev, val])
}, []);
return (
<div className="container">
<div>
<h1>Items To Buy</h1>
<List itemsArray={buyList} onUpdateClick={updateBuyClick} buttonText="Add Items to Buy" idx={"list One"}></List>
</div>
<div>
<h1>Items to Sell</h1>
<List itemsArray={sellList} onUpdateClick={updateSellClick} buttonText="Add Items to Sell" idx={"list Two "}></List>
</div>
</div>
);
}
const List = React.memo(function List({ itemsArray = [], buttonText, onUpdateClick, idx }) {
// *** `currentSell` stuff should be in state, not a local variable
const [currentSell, setCurrentSell] = useState("");
console.log(`${idx} is being rendered`);
return ( // <>...</> is fine, I had to change it because the
// version of Babel Stack Snippets use is out of date
<React.Fragment>
<ul>
{itemsArray.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
<div>
<input type="text" onChange={(e) => { setCurrentSell(e.target.value); }}></input>
<button onClick={() => { onUpdateClick(currentSell); }}>{buttonText}</button>
</div>
</React.Fragment>
);
});
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

How to make a react js element by using props?

I have a functional element in react js like this,
function FilterOptions() {
const [isShown, setIsShown] = useState(false);
return (
<div className="filter__options">
{["Category", "Design", "Size", "Style"].map((ourOption) => (
<div
onMouseEnter={() => setIsShown(true)}
onMouseLeave={() => setIsShown(false)}
className="filter__options__container"
>
<div className="filter__options__button">
{ourOption}
</div>
{isShown && <div className="filter__options__content"> Here I want to return the element using props </div>}
</div>
))}
</div>
);
}
I have created a files called, Category.js, Design.js, Size.js, Style.js.
Now I want to use the props so that I can concatenate like this <{ourOption}> <{ourOption}/> so that this will return element.
Any idea how to do this guys?
Choosing the Type at Runtime
First: Import the components used and create a lookup object
import Category from 'Category';
import Design from 'Design';
import Size from 'Size';
import Style from 'Style';
// ... other imports
const components = {
Category,
Design,
Size,
Style,
// ... other mappings
};
Second: Lookup the component to be rendered
function FilterOptions() {
const [isShown, setIsShown] = useState(false);
return (
<div className="filter__options">
{["Category", "Design", "Size", "Style"].map((ourOption) => {
const Component = components[ourOption];
return (
...
<div className="filter__options__button">
<Component />
</div>
...
))}}
</div>
);
}
Alternatively you can just import and specify them directly in the array to be mapped.
function FilterOptions() {
const [isShown, setIsShown] = useState(false);
return (
<div className="filter__options">
{[Category, Design, Size, Style].map((Component) => (
...
<div className="filter__options__button">
<Component />
</div>
...
))}
</div>
);
}
Instead of strings you could iterate over Array of Components
{[Category, Design, Size, Style].map((Component) => (
<Component/>
);
Ill do this as react document
//create components array
const components = {
photo: Category,
video: Design
.....
};
{
Object.keys(components).map((compName) => {
const SpecificSection = components[compName];
return <SpecificSection />;
})
}
Here is a small sample code that you can work with. Use direct component instead of trying to determine by strings.
const Comp1 = () => {
return <p>Comp1 Here</p>
}
const Comp2 = () => {
return <p>Comp 2 Here</p>
}
export default function App() {
return (
<div className="App">
{[Comp1, Comp2].map(Komponent => {
// use Komponent to prevent overriding Component
return <Komponent></Komponent>
})}
</div>
);
}

About route(link) and asynchronous fetch data in react

First of all, good evening. I'm trying to improve myself at React. So I'm working on a Starwars project 👨‍💻.
I have two problems.
First of all, I listed different characters at the bottom of my character detail page. Again, I want it to be directed to different characters through the same component. But even if the link changes, my component is not refreshed. But the picture is changing 🤔.
Note:
sandbox link : https://codesandbox.io/s/github/kasim444/Javascript-Camp-2019/tree/master/challenges/star-wars-app/
my project github link : https://github.com/kasim444/Javascript-Camp-2019/tree/master/challenges/star-wars-app/
// component that I redirect in.
class CharacterDetail extends Component {
render () {
const characterId = this.props.match.params.id;
const {
name,
height,
mass,
hair_color,
skin_color,
eye_color,
birthday_year,
gender,
homeworld,
loading,
} = this.state;
return loading
? <Loading />
: (
<div>
<main className="characterBg">
<DetailHeader imgLink={characterAvatarLink[characterId - 1]} />
<CharacterContent
imgLink={characterAvatarLink[characterId- 1]}
characterInfo={this.state}
/>
</main>
<FeaturedCharacters />
</div>
);
}
}
// feautered character component
function FeaturedCharacters () {
const [characters, setCharacters] = useState ([]);
const [loading, setLoading] = useState (true);
const fetchCharacters = async () => {
const data = await fetch ('https://swapi.co/api/people/');
const fetchPeople = await data.json ();
const feauteredCharacter = fetchPeople.results.filter (
(character, index) => index < 4
);
setCharacters (feauteredCharacter);
setLoading (false);
};
useEffect (() => {
fetchCharacters ();
}, []);
return (
<div>
<h2>Popular Characters</h2>
<div className="d-flex-row container">
{loading
? <PlaceholderDiv />
: characters.map ((character, index) => (
<CharacterCard
key={character.name}
chaId={index + 1}
chaDet={character.name}
imgLink={characterAvatarLink[index]}
/>
))}
</div>
</div>
);
}
// character card link component
const CharacterCard = props => {
const name = props.chaDet;
return (
<Link className="profile_card" to={`/character/${props.chaId}`}>
<div className="profile_image">
<img src={props.imgLink} alt={name} />
</div>
<div className="profile_content">
<h3>{name}</h3>
<div className="read_more d-flex-row">
<img src={showIcon} alt="Show Icon" />
Show More
</div>
</div>
</Link>
);
};
// main component
const App = () => {
return (
<Router>
<div className="st-container d-flex-column">
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/movie/:title" component={MovieDetails} />
<Route path="/character/:id" component={CharacterDetail} />
<Route
path="/githubProfile"
component={() => {
window.location.href = 'https://github.com/kasim444/Javascript-Camp-2019/tree/master/challenges/star-wars-app';
return null;
}}
/>
</Switch>
<Footer />
</div>
</Router>
);
};
My second problem is that I draw a data back from the data from Api. I can reach outlines of the character. It's working now. But I feel that there are some things that don't feel right.🤨 How can I improve Fetch operations in Axios?
async componentDidMount () {
const characterId = this.props.match.params.id;
const filmSeries = [];
const characterDetail = await axios.get (
`https://swapi.co/api/people/${characterId}/`
);
const filmsFetchLinks = characterDetail.data.films;
const promisesData = await filmsFetchLinks.map(link => axios.get(link));
axios.all (promisesData).then(value => {
value.map (val => filmSeries.push (val.data.title));
let {
name,
height,
mass,
hair_color,
skin_color,
eye_color,
birthday_year,
gender,
homeworld,
films,
} = characterDetail.data;
fetch(homeworld).then(home => home.json()).then(val => this.setState({homeworld: val.name}));
this.setState ({
name,
height,
mass,
hair_color,
skin_color,
eye_color,
birthday_year,
gender,
films: filmSeries,
loading: false,
});
});
}
I'm sorry if I bored you. It seems a little long because the components are interconnected. Thank you in advance for your interest. 🖖 🙏
You can use componentDidUpdate and compare the the current parameter id to the previous one (you would have to save it to state) and you fetch data again IFF the two are different. componentDidUpdate will go every time the route changes.
A better approach would be to use useEffect and depend on the parameter id. In the use effect, you do all your data fetching. Something like this:
const { id: characterId } = props.match.params;
React.useEffect(() => {
async function getData() {
// fetch all you data here
}
getData();
}, [characterId]);
You can see a crude version of this here:
https://codesandbox.io/s/star-wars-app-sx8hk

Categories

Resources