I have problem with load data to component after click on button.
I use getInitialProps to first load data on page.
How to load new data and past them to {data} after click?
export default function Users({ data }) {
const fetchData = async () => {
const req = await fetch("https://randomuser.me/api/?gender=male&results=100");
const data = await req.json();
return { data: data.results };
};
const handleClick = (event) => {
event.preventDefault();
fetchData();
};
return (
<Layout>
<button onClick={handleClick}>FETCH DATA</button>
{data.map((user) => {
return (
<div>
{user.email}
<img src={user.picture.medium} alt="" />
</div>
);
})}
</Layout>
);
}
Users.getInitialProps = async () => {
const req = await fetch(
"https://randomuser.me/api/?gender=female&results=10"
);
const data = await req.json();
return { data: data.results };
};
Thank a lot for help!
Use useState with the default value being the data you initially retrieved via getInitialProps:
import { useState } from 'React';
export default function Users({ initialData }) {
const [data, setData] = useState(initialData);
const fetchData = async () => {
const req = await fetch('https://randomuser.me/api/?gender=male&results=100');
const newData = await req.json();
return setData(newData.results);
};
const handleClick = (event) => {
event.preventDefault();
fetchData();
};
return (
<Layout>
<button onClick={handleClick}>FETCH DATA</button>
{data.map((user) => {
return (
<div>
{user.email}
<img src={user.picture.medium} alt="" />
</div>
);
})}
</Layout>
);
}
Users.getInitialProps = async () => {
const req = await fetch('https://randomuser.me/api/?gender=female&results=10');
const data = await req.json();
return { initialData: data.results };
};
Sidenote: Times have changed and it would seem that user1665355 is indeed correct:
Recommended: getStaticProps or getServerSideProps
If you're using Next.js 9.3 or newer, we recommend that you use
getStaticProps or getServerSideProps instead of getInitialProps.
These new data fetching methods allow you to have a granular choice
between static generation and server-side rendering.
import { useState } from 'React';
export default function Users({ initialData }) {
const [data, setData] = useState(initialData);
const fetchData = async () => {
const req = await fetch('https://randomuser.me/api/?gender=male&results=100');
const newData = await req.json();
setData(newData.results);
};
const handleClick = (event) => {
event.preventDefault();
fetchData();
};
return (
<Layout>
<button onClick={handleClick}>FETCH DATA</button>
{data.map(user => {
return (
<div key={user.login.uuid}>
{user.email}
<img src={user.picture.medium} alt="" />
</div>
);
})}
</Layout>
);
}
Users.getInitialProps = async () => {
const req = await fetch('https://randomuser.me/api/?gender=female&results=10');
const data = await req.json();
return { initialData: data.results };
};
I would like to list my notes about George's code. At least, it should pay attention to them.
First of all, it should attach any key to a div element otherwise a warning will have appeared in the browser console. Here is an article about using keys: https://reactjs.org/docs/lists-and-keys.html#keys
As well, the keyword return can be removed from the fetchData function that doesn't return a response.
It is recommended to use getStaticProps or getServerSideProps now. https://nextjs.org/docs/api-reference/data-fetching/getInitialProps
Related
for learning purposes I'm creating a CRUD todo list with React and JSON-server. I got stuck with PATCH method, as it only updates data in JSON-server on the first click. I want to update the data with the component's state value.
Service file with requests:
const serverAddress = 'http://localhost:8000';
const collection = 'todoItems';
const fetchAll = async () => {
const response = await fetch(`${serverAddress}/${collection}`);
const todoItems = response.json();
return todoItems;
};
const complete = async (id) => {
const completed = {
completed : true
}
// how to set the 'completed' value in json-server based on item's state?
const response = await fetch(`${serverAddress}/${collection}/${id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(completed ),
});
const data = await response.json();
return data;
}
const TodoItemsService = {
fetchAll,
create,
remove,
complete
};
export default TodoItemsService;
Card component which holds all todo items:
const Card = () => {
const [todoItems, setTodoItems] = useState([]);
const fetchAllTodoItems = async () => {
const fetchedTodoItems = await TodoItemsService.fetchAll();
setTodoItems(fetchedTodoItems);
};
useEffect(() => {
(async () => {
fetchAllTodoItems();
})();
}, []);
const handleComplete = async (id) => {
await TodoItemsService.complete(id);
}
return (
<div className='card'>
<CardHeader />
<AddTodoForm onAddTodoItem={handleAddTodoItem} />
<TodoItemsContainer
todoItems={todoItems}
onDelete={handleDelete}
onComplete={handleComplete}
/>
</div>
)
}
export default Card;
TodoItemsContainer component
const TodoItemsContainer = ({ todoItems, onDelete, onComplete }) => {
return (
<div className='todo-items-container'>
{todoItems.length === 0 &&
<div className='empty'>
<img src={NoTodoItems} alt="" />
</div>}
{todoItems.map(({ id, text }) => (
<TodoItem
key={id}
id={id}
text={text}
onDelete={onDelete}
onComplete={onComplete}
/>
))}
</div>
)
}
export default TodoItemsContainer;
TodoItem component
const TodoItem = ({ id, text, onDelete, onComplete }) => {
const [isComplete, setIsComplete] = useState(false);
const handleIsCompleteById = () => {
onComplete(id);
setIsComplete(!isComplete);
};
const handleDeleteTodoItemById = () => {
onDelete(id);
};
return (
<div className={`todo-item ${isComplete ? 'complete' : ''}`}>
<p>{text}</p>
<div>
<TodoItemComplete onComplete={handleIsCompleteById}/>
<TodoItemDelete onDelete={handleDeleteTodoItemById}/>
</div>
</div>
)
}
export default TodoItem;
TodoItemComplete button component
const TodoItemComplete = ({ onComplete }) => {
return (
<button type='button' onClick={onComplete}>
<div className='icon'>
{<SVGComplete />}
</div>
</button>
)
}
export default TodoItemComplete;
From React perspective it works fine, it marks the item as complete based on state, but I also want to reflect todo item's status as complete in my json-server. Does anyone have any tips or can see the mistake?
Simply had to pass the state as the second param in complete service and other functions that handle complete action.
import { useState, useEffect } from 'react';
import axios from 'axios'
import { Loading } from './loading';
function News({ pageSize }) {
const [isLoading, setIsLoading] = useState(false)
const [state, setState] = useState({
article: [],
page: 1
}
)
const getUsers = async () => {
setIsLoading(true)
let res = await axios.get(`https://newsapi.org/v2/everything?domains=wsj.com&apiKey=79b02b430c1946cd9c505d3f91d7aec6&page=1&pageSize=${pageSize}`);
setState({article: res.data.articles})
setIsLoading(false)
};
useEffect(() => {
getUsers()
}, [])
const handleNext = async () => {
setIsLoading(true)
let res = await axios.get(`https://newsapi.org/v2/everything?domains=wsj.com&apiKey=79b02b430c1946cd9c505d3f91d7aec6&page=${state.page + 1}&pageSize=${pageSize}`);
setState({article: res.data.articles, page: state.page + 1})
setIsLoading(false)
}
let data = Array.from(state.article)
return (
<div>
<h2>News</h2>
<button onClick={handleNext}>Next</button>
{isLoading && <Loading />}
{!isLoading && data.map((elements) => {
return (
<div key={elements.url} style={{ marginBottom: '2rem' }}>
<div> {elements.description} </div>
<div>{new Date(elements.publishedAt).toGMTString()}</div>
</div>
)
})}
</div>
);
}
export default News;
When I take states separately for data and page, I'm able to display next page's data. But now that I've created one state to manage multiple objects, it displays back first page's data instead of next page's data. I don't know what I'm doing wrong. Pls help me!
Ignore the redundancy.
Try this:
const getUsers = async () => {
setIsLoading(true)
let res = await axios.get(`https://newsapi.org/v2/everything?domains=wsj.com&apiKey=79b02b430c1946cd9c505d3f91d7aec6&page=1&pageSize=${pageSize}`);
setState({...state, article: res.data.articles})
setIsLoading(false)
};
I have 2 APIs, one call the products and the other one call the image of the products.
Products API: https://inventory.dearsystems.com/ExternalApi/v2/Product
The second API is required call the ID of the products
Image API: https://inventory.dearsystems.com/ExternalApi/v2/product/attachments?ProductID=
How can I call the second one to show the image.
Here is my code:
import axios from "axios";
import { useEffect, useState } from "react";
import { SingleContent } from "../../components/SingleContent/SingleContent";
export const All = () => {
const [content, setContent] = useState([]);
const fetchAllProducts = async () => {
const { data } = await axios.get('https://inventory.dearsystems.com/ExternalApi/v2/Product',{
headers: {
"api-auth-accountid": process.env.REACT_APP_API_ID,
"api-auth-applicationkey": process.env.REACT_APP_API_KEY
}
});
console.log(data.Products);
setContent(data.Products)
}
useEffect(() =>{
fetchAllProducts();
}, [])
return (
<div>
<h1 className="pageTitle">All Products</h1>
<div className="all">
{content && content.map((c) =>
<SingleContent
key={c.id}
id={c.ID}
name={c.Name}
sku={c.SKU}
category={c.Category}/> )}
</div>
</div>
)
}
Inside fetchAllProducts() you could map the data.Products array you get from the first request, call the second api for each item and add the product image to the item.
Then you can update the contents with the resulting array.
Edited: example code below.
const fetchAllProducts = async () => {
const { data } = await axios.get('https://inventory.dearsystems.com/ExternalApi/v2/Product',{
headers: {
"api-auth-accountid": process.env.REACT_APP_API_ID,
"api-auth-applicationkey": process.env.REACT_APP_API_KEY
}
});
const productsWithImage = data.Products
.map(async product => {
const imageUrl = await axios.get("https://inventory.dearsystems.com/ExternalApi/v2/product/attachments?ProductID=" + product.id)
return {...product, imageUrl }
})
setContent(productsWithImage)
}
// Then you can use product.imageUrl when you map your products
Use for instead of map because it was returning a Promise. The problem is that if setContent(result) is called outside of the for, it only returns on register.
See the code:
export const AllHandle = () => {
const [content, setContent] = useState([]);
const fetchAllProducts = async () => {
const { data } = await axios.get("https://inventory.dearsystems.com/ExternalApi/v2/Product?limit=10",{
headers: {
"api-auth-accountid": process.env.REACT_APP_API_ID,
"api-auth-applicationkey": process.env.REACT_APP_API_KEY
}
});
let result = [];
const totaProducts = await data.Products;
for (let product of totaProducts) {
const imageUrl = await axios.get(`https://inventory.dearsystems.com/ExternalApi/v2/product/attachments?ProductID=${product.ID}`, {
headers: {
"api-auth-accountid": process.env.REACT_APP_API_ID,
"api-auth-applicationkey": process.env.REACT_APP_API_KEY
}
});
const imgUrl = imageUrl.data
result = [{ ...product, imgUrl}]
}
setContent(result)
}
useEffect(() =>{
fetchAllProducts();
}, [])
return (
<div>
<h1 className="pageTitle">All Products</h1>
<div className="all">
{content && content.map((c) =>
<SingleContent
key={c.ID}
id={c.ID}
name={c.Name}
sku={c.SKU}
category={c.Category}
attachment= { c.imgUrl[0].DownloadUrl}
/>
)}
</div>
</div>
)
}
I'm creating a page that will call my API route to return the value from my collection using MongoDB. But I'm having this error of Objects are not valid as a React child. I don't know why this happening. Can you please help me?
pages/index.js
export const getServerSideProps = async () => {
const res = await fetch('http://localhost:3000/api/listings');
const data = await res.json();
if (!data) {
return {
notFound: true,
};
}
return { props: { data } };
};
const index = async ({ data }) => {
return (
<>
<section className='w-screen h-screen bg-hero-pattern bg-cover bg-no-repeat bg-bottom'></section>
{data.map((prop) => (
<div key={prop._id}>
<h1>{prop.name}</h1>
<h2 className='text-2xl truncate'>{prop.summary}</h2>
<p>{prop.description}</p>
</div>
))}
</>
);
};
pages/api/listings
import { connectToDatabase } from '../../middlewares/mongodb';
export const fetchDbData = async () => {
const { db } = await connectToDatabase();
const data = await db
.collection('listingsAndReviews')
.find({})
.limit(1)
.toArray();
return JSON.parse(JSON.stringify(data));
};
export default async (req, res) => {
const data = await fetchDbData();
res.status(200).json(data);
};
I am building a Simple ToDoList App.
I fetch List Using React Hook.
When I add a new Todo or delete an existing one the request sends and works but the component doesn`t rerender.
I tried 2 ways to solve the problem to create
1.async functions(delete and add). Took getToDoList outside the hook and call it after requests(post/delete)
useEffect(() => {
getToDoList();
}, []);
const getToDoList = async () => {
const result = await axios.get('http://localhost:1200/');
setToDoList(result.data);
};
const addNewTodo = async () => {
await axios.post('http://localhost:1200/create', {
item: newToDo.current.value
});
getToDoList();
};
const deleteToDo = async (id) => {
await axios.delete(`http://localhost:1200/delete?id=${id}`);
getToDoList();
};
2.Took getToDoList inside the hook and gave it 2 dependecies which change in deleteToDo/addNewToDo
const [add, setAdd] = useState(false);
const [remove, setRemove] = useState(false);
useEffect(() => {
const getToDoList = async () => {
const result = await axios.get('http://localhost:1200/');
setToDoList(result.data);
};
getToDoList();
}, [add, remove]);
const addNewTodo = async () => {
await axios.post('http://localhost:1200/create', {
item: newToDo.current.value
});
setAdd(!add);
};
const deleteToDo = async (id) => {
await axios.delete(`http://localhost:1200/delete?id=${id}`);
setRemove(!remove);
};
Both don`t work. Tell me please where Im wrong
JSX
return (
<div>
<Jumbotron>
<Container>
<h1>Hello!</h1>
<p>This is a simple ToDoList created by Vadik</p>
</Container>
</Jumbotron>
<Container>
<Container>
<InputGroup>
<FormControl placeholder="What needs to be done" ref={newToDo}/>
</InputGroup>
<Button onClick={addNewTodo} variant="primary" size="sm">
Add new Todo
</Button>
</Container>
<Container className="cards">
<Card>
<ListGroup>
{toDoList.todos.map((elem) => <ListGroup.Item
key={elem._id}>{elem.item}<CloseButton onClick={() => deleteToDo(elem._id)}/> </ListGroup.Item>)}
</ListGroup>
</Card>
</Container>
</Container>
</div>
);
Try the below code.This should work.In addTodo there are 2 ways either update the resultList after post is successful or use the getList to get the latest data and update the resultList. Both of them are shown.
function Example() {
const [resultList, setResult] = useState({todos: []});
useEffect(() => {
const todoList = getToDoList();
setResult(prev => todoList);
}, []);
const addTodo = async () => {
await axios.post('http://localhost:1200/create', {
item: newToDo.current.value
});
//Addition successful, hence update our resultList.
return setResult(prev => {
return {todos: [...prev.todos, {item: newToDo.current.value, id: prev.todos.length+1}]}
});
//Now since add api doesn't return a
//value call Get api again and update the todoList to render
//const todoList = getToDoList();
//setResult(prev => todoList);
};
const getToDoList = async () => {
const result = await axios.get('http://localhost:1200/');
return result.data;
};
return (
<div id='container'>
{resultList.todos.map(todo => <div>{todo.item}</div>
)}
<button onClick={addTodo}>Add</button>
</div>
)
}
The problem was that I didn`t send any response to those requests.enter image description here