My array of API generated "todo" objects.
That is console logged, but i have also saved it as a variable, todosData. Now this variable used to be the same format( array of objects with id, title, completed ) but my hardcoded data. I rendered this with components as my app is made with react. This is the code for it:
import React from "react";
import TodoItem from "./TodoItem";
import todosData from "./TodosData";
// Container for every todo object
export default function Todos() {
const todoItemArray = todosData.map((todo) => {
return <TodoItem title={todo.title} key={todo.id} completed={todo.completed} />;
});
return <div>{todoItemArray}</div>;
}
Now as you can see i havent even changed the array name when i switched from hardcoded data to api data. Both were an array of objects, as i mentioned. Just this map method is rendered 0 components to my website. Before it rendered all ( when i hardcoded the values ).
Im completely confused.
This is the fetch() method to get the data. Even though my console.log shows that isnt the problem:
let todosData = [];
fetch("https://jsonplaceholder.typicode.com/posts/")
.then((res) => res.json())
.then((data) => todosData.push(...data))
.then(() => console.log(todosData));
export default todosData;
You can't just store your data in a variable. React does not know when you mutate it. You need to use component state so that react knows when to re-render your component. Also the place to fetch data is in an effect:
export default function Todos() {
const [todos, setTodos] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts/")
.then(res => res.json())
.then(data => setTodos(data))
}, []);
return (
<div>
{todos.map(todo => (
<TodoItem title={todo.title} key={todo.id} completed={todo.completed} />
)}
</div>;
}
Related
I'm using Redux Toolkit and Redux Thunk. I successfully get my data asynchronously from a data source back into my component. Inside my component, I can observe the data coming with useSelector: const booksData = useSelector((state) => state.books);
At this point, I would like to loop the books I'm getting and render my Book component for each book data I'm getting:
const renderBooks = (books) => {
books.map((book) => {
console.log(book.id);
<Book props={book}/>;
});
};
Problem is, I can see this renderBooks is called and bookIds logged for each book. The <Book> component is not rendered though.
Am I missing something obvious? Isn't the useSelector a state like useState so it should trigger rendering once updated?
Thanks in advance.
Rendering code (simplified to cut noise):
return (
<React.Fragment>
{/*some usual HTML here */}
<div>{renderBooks(booksData.books)}</div>
</React.Fragment>
);
You need to return the values:
const renderBooks = (books) => {
return books.map((book, index) => {
console.log(book.id);
return <Book key={index} props={book}/>;
});
};
This is how my app looks like right now:
The search and sort functions are working dynamically, as soon as I type a letter, it finds the match but re-renders all of them.
If I type "hunger", it finds the hunger games films, but it's getting images and rendering when it already has them. Is there any way to make this process just for once so I don't wait for every search, every sorting? I use Redux so data is coming from store, store is getting the data from local json file.
I thought about storing on local storage but I couldn't figure it out. As you can see there is no ComponentDidMount or Hooks.
This is the code of this page:
import React from "react";
import "./MoviesAndSeries.css";
import ListShowItem from "./ListShowItem";
import { getVisibleShows } from "../Redux/Selector";
import { connect } from "react-redux";
import FilterShowItems from "./FilterShowItems";
const Movies: React.FC = (props: any) => {
return (
<div className="movies">
<FilterShowItems />
{props.movies.map((movie: any) => {
return <ListShowItem key={Math.random()} {...movie} />;
})}
</div>
);
};
const mapStateToProps = (state: any) => {
return {
movies: getVisibleShows(state.movies, state.filters),
filters: state.filters,
};
};
export default connect(mapStateToProps)(Movies);
You are using Math.random() as keys. The keys change all the time, and React can't know if the items already exist, so it re-renders all of them:
<ListShowItem key={Math.random()} {...movie} />;
Change the key to something stable, and unique, the movie id (if you have one) for example:
<ListShowItem key={movie.id} {...movie} />;
I'm trying to pass props from my Parent component to child component. Here are the important snippets:
Snippet 1: This is the object that contains the prop (integer).
const cardProps = {
cardProps0: 0,
Snippet 2: This is the Card component within the parent component that carries the prop to child component
return (
<MyCardLink source={cardProps.cardProps0} />
Snippet 3: This is the child component (MyCardLink)
useEffect((props) => {
axios
.get(
'http://newsapi.org/v2/everything?q=economy&apiKey=XXXXXXXXXXXXXXXX'
)
.then((res) => {
console.log(res);
setNews(res.data.articles[props.source]);
})
.catch((err) => {
console.log(err);
});
}, []);
The goal is that [prop.source] contains a number value from a list of an array served by an API. If I just place a number value in the child component (MyCardLink) in place of [props.source] on the setNews function then it renders the component no problem.
My problem is when I pass the prop from parent component to child component and use [prop.source], nothing renders and all I get from the console log is:
Cannot read property 'source' of undefined.
Am I doing something wrong?
Instead of passing props into your useEffect, you need to add into your MyCardLink component's parameters as:
const MyCardLink = (props) => {
// your component's defintion
}
Additionally you can destructure as the following:
const MyCardLink = (props) => {
const { source } = props
// ... rest
}
Then simply you can use in your useEffect without props as:
useEffect(() => {
axios.get(
'http://newsapi.org/v2/everything?q=economy&apiKey=XXXXXXXXXXXXXXXX'
)
.then((res) => {
console.log(res);
setNews(res.data.articles[source]);
})
.catch((err) => {
console.log(err);
}
);
}, []);
Based on your other question from the comment section what I would do is:
Change the initial value of the state from "" to null as const [news, setNews] = useState(null).
Also I would use && for null check and render <Card /> component only if it has value as news && <Card className={classes.root}> in your return.
The reason behind this is your API response is arriving asynchronously.
use use props in component below:
const MyCardLink =(props)=>{
...
...
...
}
export default MyCardLink;
I am fetching a list of directories via firebase storage, and then once that has finished, return a list of Project components to be rendered, each with the name retrieved before. How do you first wait for the fetching to complete and then return the same number of Project components?
This is what I have tried:
import React, {useState} from "react"
import "./index.css"
import {hot} from "react-hot-loader"
import Project from "./Project"
import firebase from "./Firebase.js"
function Projects(props){
// Get refereance to firebase storage
let storage = firebase.storage();
// Reference the main projects folder
let storageRef = storage.ref().child('projects');
// Store all project names from firebase storage to then return them as Project components
let projects = []
// This lists the directories in 'Projects'
storageRef.listAll().then(res => {
// For each directory, push the name into the projects array
res.prefixes.forEach((folderRef) => {
projects.push(folderRef.name)
})
}).catch(error => {
console.log(error)
})
return(
<div>
{projects.map(projName => (
<>
<Project project={projName}/>
</>
))}
</div>
)
}
export default hot(module)(Projects)
However, when projects is returned, it is empty as it hasn't waited for the forEach to finish above. Plus, I don't think the return statement within projects.map() is working. I have attempted a Promise and Async Await but am not sure how to structure it.
Similar to class components, you will need to define your state using the useState hook in functional components.
In addition, you should use the useEffect hook to handle the carrying out the HTTP request such that it will not be triggered on every re-render. We added an empty array ([]) as the second parameter of the useEffect hook, such that it will only be run once, as explained on the official React hooks documentation.
After the responses have been returned, we update the state by calling setProjects().
function Projects(props){
const [ projects, setProjects ] = useState([]);
let storage = firebase.storage();
let storageRef = storage.ref().child('projects');
useEffect(() => {
const response = []
storageRef.listAll().then(res => {
// For each directory, push the name into the projects array
res.prefixes.forEach((folderRef) => {
response.push(folderRef.name)
})
setProjects(response);
}).catch(error => {
console.log(error)
})
}, [])
return(
<div>
{projects.length && projects.map((projName, index) => (
<Project project={projName} key={index} />
))}
</div>
)
}
export default hot(module)(Projects)
I'm trying to fetch data from a server using the fetch api and displaying this data in an unordered list using uuidv4 as the key, im trying to do this using react's useEffect hook, however, it fetches the data but then it says all my keys are the same, i think useEffect is the cause and i tried adding as the second argument an empty array to prevent the rerendering but then its saying im missing a dependency and when i put the dependency in it rerenders and the uuid's are the same again. i'm not sure what to do im relatively new to hooks.
import React, { useState, useEffect } from "react";
import "./App.css";
const uuidv4 = require("uuid/v4");
function App() {
const [country, setCountry] = useState({
country: []
});
useEffect(() => {
console.log("fetching data");
fetch("http://localhost:5000/country")
.then(res => res.json())
.then(data => setCountry({...country, country: data }));
}, []);
return (
<>
<ul>
{country.country.map(item => {
return <li key={uuidv4}>{item.name}</li>;
})}
</ul>
</>
);
}
export default App;
I think you have to call it to generate the uuid:
return <li key={uuidv4()}>{item.name}</li>;
Best way to assign or set the keys when you map over a list is to use index, which you can get from map
return (
<>
<ul>
{country.country.map((item, index) => {
return <li key={index}>{item.name}</li>;
})}
</ul>
</>
);