Why can't clear error message after show - axios - react - javascript

First question for context
I'm showing using with localhost:3000/users/:id
API has 10 user so if request to localhost:3000/users/11 should show error message
Also want to show message for connection problems too
I've tried to add setError(""); inside finally block but error message stopped working.
If I don't add this time working when I get error but when I fix error error still appear.
I want to show user details again
import { useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import axios from "axios";
function User() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
axios("https://jsonplaceholder.typicode.com/users/" + id)
.then((res) => setUser(res.data))
.catch((error) => setError(error.message))
.finally(() => setIsLoading(false));
}, [id]);
return (
<div>
{error === "Request failed with status code 404" ? (
<p>{error}</p>
) : isLoading ? (
<h2>Loading...</h2>
) : (
<div>
<h3>User Info</h3>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
)}
</div>
);
}
export default User;

You have to clear your error when the response is OK
.then((res) => {
setUser(res.data);
setError("")
})
import { useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import axios from "axios";
function User() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
axios("https://jsonplaceholder.typicode.com/users/" + id)
.then((res) => {
setUser(res.data);
setError("")
})
.catch((error) => setError(error.message))
.finally(() => setIsLoading(false));
}, [id]);
return (
<div>
{error === "Request failed with status code 404" ? (
<p>{error}</p>
) : isLoading ? (
<h2>Loading...</h2>
) : (
<div>
<h3>User Info</h3>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
)}
</div>
);
}
export default User;

Related

React - Axios Showing Message to User

want to show message to user. console log working fine im not sure why i cant show to user.
tried several methods to fix.
error message from axios = 'Request failed with status code 404'
import { useParams, Link, Route, Routes } from 'react-router-dom';
import { useState, useEffect } from 'react';
import axios from 'axios';
function User() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
console.log("error", error);
useEffect(() => {
axios("https://jsonplaceholder.typicode.com/users/" + id)
.then(res => setUser(res.data))
.catch(error => {
console.log("Error=>", error);
console.log(error.message);
setError(typeof (error.message));
console.log("Check",error.message == 'Request failed with status code 404');
})
.finally(() => {
setIsLoading(false);
});
}, [id]);
return (
<div>
{error.message == 'Request failed with status code 404' ? <p>{error.message}</p> :
isLoading ? <h2>Loading...</h2> :
<div>
<h3>User Info</h3>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
}
<Link to={`/users/${parseInt(id) + 1}`}><button>Next User</button></Link>
</div>
)
}
export default User
You have a couple bugs:
use === when comparing instead of == to avoid type coercion
when setting state setError(typeof (error.message));, remove typeof of it , setError(error.message);
when testing the error.message simply test error and also display error
Note: I removed <Link> only for demo to stop throwing errors on Routes
import { useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import axios from "axios";
function User() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
axios("https://jsonplaceholder.typicode.com/users/" + id)
.then((res) => setUser(res.data))
.catch((error) => setError(error.message))
.finally(() => setIsLoading(false));
}, [id]);
return (
<div>
{error === "Request failed with status code 404" ? (
<p>{error}</p>
) : isLoading ? (
<h2>Loading...</h2>
) : (
<div>
<h3>User Info</h3>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
)}
</div>
);
}
export default User;
Try to re-organize your logical rendering check the conditions
import { useParams, Link, Route, Routes } from 'react-router-dom';
import { useState, useEffect } from 'react';
import axios from 'axios';
function User() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
console.log("error", error);
useEffect(() => {
axios("https://jsonplaceholder.typicode.com/users/" + id)
.then(res => setUser(res.data))
.catch(error => {
console.log("Error=>", error);
console.log(error.message);
// set error instead of type of the error
setError(error);
console.log("Check",error.message == 'Request failed with status code 404');
})
.finally(() => {
setIsLoading(false);
});
}, [id]);
return (
<div>{loading?
<h2>Loading...</h2>
:
<div>
{error?.message ===null?
<p>'Request failed with status code 404'<p> : <p>{error.message}</p>}
<h3>User Info</h3>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
</div>
}
<Link to={`/users/${parseInt(id) + 1}`}><button>Next User</button></Link>
</div>
)
}
export default User
Seeing as you are using empty string in the state, just pass the error.message directly, and then your error will contain a string.
function User() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
console.log("error", error);
useEffect(() => {
axios("https://jsonplaceholder.typicode.com/users/" + id)
.then(res => setUser(res.data))
.catch(error => {
console.log("Error=>", error);
console.log(error.message);
setError(error.message); // change this
console.log("Check",error.message == 'Request failed with status code 404');
})
.finally(() => {
setIsLoading(false);
});
}, [id]);
return (
<div>
{/* and change this as well */}
{error ? <p>{error.message}</p> :
isLoading ? <h2>Loading...</h2> :
<div>
<h3>User Info</h3>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
}
<Link to={`/users/${parseInt(id) + 1}`}><button>Next User</button></Link>
</div>
)
}

How to show search result not found message

import React, { useState, useEffect } from 'react'
import axios from 'axios'
function DataApi({ searchTerm }) {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState()
useEffect(() => {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
setUsers(res.data);
console.log(res.data);
setLoading(true);
})
.catch(error => {
console.log(error);
setError('Error retrieving data');
});
}, []);
return (
<div>
<div>
{
!loading ?
<h1>...Loading</h1>
:
users.length > 0 && users.filter((item) =>
(searchTerm === '') ? item :
(item.title.toLowerCase().includes(searchTerm.toLocaleLowerCase())) ? item :
// <h1>search result not found</h1>
null
).map((item) => {
return (
<ul key={item.id}>
<li>Name: {item.id}</li>
<li>Title: {item.title}</li>
<li>Body: {item.body}</li>
</ul>
)}
)
}
{
error ? <h1>{error}</h1> : null
}
</div>
</div>
)
}
export default DataApi;
I have made a search field in which user can search the name of the person. If user does not get the searched name then there should be a message come that search result not found. I tried to implement it using if-else (ternary operator) & put the message into else part but it is not working. When I put null instead of search result not found then it works perfectly but I am not able to show the message then. But if I put search result not found instead of null then nothing works, not even filter functionality. Can you guys please help me? Thank you in advancve.
You can simply check the length of user and move the filter method to the useEffect and show a message
import React, { useState, useEffect } from "react";
import axios from "axios";
function DataApi({ searchTerm }) {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const [searchTermTest, setsearchTermTest] = useState();
function handleChange(event) {
setsearchTermTest(event.target.value);
}
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((res) => {
const data = res.data;
const filteredData = data.filter((dat) =>
dat.title.includes(searchTermTest === undefined ? "" : searchTermTest)
);
setUsers(filteredData);
setLoading(true);
})
.catch((error) => {
console.log("errr", error);
setError("Error retrieving data");
});
}, [searchTermTest]);
return (
<div>
<input type="text" onChange={handleChange} />
<div>
{!loading ? (
<h1>...Loading</h1>
) : (
users.length > 0 &&
users.map((item) => {
return (
<ul key={item.id}>
<li>Name: {item.id}</li>
<li>Title: {item.title}</li>
<li>Body: {item.body}</li>
</ul>
);
})
)}
{users.length === 0 && loading ? <h1>search result not found</h1> : ""}
{error ? <h1>{error}</h1> : null}
</div>
</div>
);
}
export default DataApi;
{users.length === 0 && loading ? <h1>search result not found</h1> : ""}
I have made it in codesandbox
Codesandbox link here
In Array.filter() method you need to return true/false value, that's how it works.
Modified the code and added the renderUser function to take care of user data filter.
DataApi function
function DataApi({ searchTerm }) {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const [searchTermTest, setsearchTermTest] = useState();
function handleChange(event) {
setsearchTermTest(event.target.value);
}
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((res) => {
const data = res.data;
const filteredData = data.filter((dat) =>
dat.title.includes(searchTermTest === undefined ? "" : searchTermTest)
);
setUsers(filteredData);
setLoading(true);
})
.catch((error) => {
console.log("errr", error);
setError("Error retrieving data");
});
}, [searchTermTest]);
return (
<div>
<input type="text" onChange={handleChange} />
<div>
{!loading ? (
<h1>...Loading</h1>
) : (
users.length > 0 && renderUsers(users, searchTerm) // updated here...
)}
{error ? <h1>{error}</h1> : null}
</div>
</div>
);
}
renderUsers function
const renderUsers = (users, searchTerm) => {
const filteredUsers = users.filter((item) => {
console.log(item.title);
return searchTerm === ""
? true
: item.title.toLowerCase().includes(searchTerm.toLocaleLowerCase())
? true
: false;
});
return filteredUsers.length > 0 ? (
filteredUsers.map((item) => {
return (
<ul key={item.id}>
<li>Name: {item.id}</li>
<li>Title: {item.title}</li>
<li>Body: {item.body}</li>
</ul>
);
})
) : (
<h1>search result not found</h1>
);
};
export default DataApi;

TypeError: Cannot read property 'params' of undefined for updating categories

So basically I'm trying to create a code that allows me to update the slug with the use of params.
Don't know why My code throws this error.
"TypeError: Cannot read property 'params' of undefined in react".
I tried replacing
useEffect(() => {
loadCategory();
}, []);
with
useEffect(() => {
if(match.params.slug) loadOrders()
}, [match.params.slug])
but it still didn't work.
This is the code I wrote.
import React, { useState, useEffect } from "react";
import {
HistoryContainer,
HistoryBg,
TextContainer2,
TextContainer3,
Text,
CatForm,
FormLabel,
FormControl,
ButtonPrimary,
} from "./CategoryUpdateElements";
import AdminNav from "../AdminNav/index";
import { toast } from "react-toastify";
import { useSelector } from "react-redux";
import { getCategory, updateCategory } from "../../../functions/category";
const CategoryUpdate = ({ history, match }) => {
const { user } = useSelector((state) => ({ ...state }));
const [name, setName] = useState("");
const [loading, setLoading] = useState(false);
useEffect(() => {
loadCategory();
}, []);
const loadCategory = () =>
getCategory(match.params.slug).then((c) => setName(c.data.name));
const handleSubmit = (e) => {
e.preventDefault();
// console.log(name);
setLoading(true);
updateCategory(match.params.slug, { name }, user.token)
.then((res) => {
// console.log(res)
setLoading(false);
setName("");
toast.success(`"${res.data.name}" is updated`);
history.push("/admin/category");
})
.catch((err) => {
console.log(err);
setLoading(false);
if (err.response.status === 400) toast.error(err.response.data);
});
};
return (
<>
<HistoryContainer>
<HistoryBg>
<AdminNav />
<TextContainer2>
<TextContainer3>
{loading ? <Text>Loading..</Text> : <Text>Update category</Text>}
<CatForm onSubmit={handleSubmit}>
<FormLabel>Name</FormLabel>
<FormControl
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
autoFocus
required
/>
<ButtonPrimary>Save</ButtonPrimary>
</CatForm>
</TextContainer3>
</TextContainer2>
</HistoryBg>
</HistoryContainer>
</>
);
};
export default CategoryUpdate;
UPDATE:
To add context to this problem. This code lets me update the name of the slug, but the TypeError doesn't let me follow through with this haha. I was actually following a tutorial regarding this and obviously, his code works. I was sure that I was following it properly as I wrote the code exactly like his but the only difference is my ui.
I also tried console logging match and after checking it out, what I saw was "undefined" which is not surprising.. It should have shown me the slug but instead it gave me "undefined".
This is his code which allows him to update his slug.
import React, { useState, useEffect } from "react";
import AdminNav from "../../../components/nav/AdminNav";
import { toast } from "react-toastify";
import { useSelector } from "react-redux";
import { getCategory, updateCategory } from "../../../functions/category";
const CategoryUpdate = ({ history, match }) => {
const { user } = useSelector((state) => ({ ...state }));
const [name, setName] = useState("");
const [loading, setLoading] = useState(false);
useEffect(() => {
loadCategory();
}, []);
const loadCategory = () =>
getCategory(match.params.slug).then((c) => setName(c.data.name));
const handleSubmit = (e) => {
e.preventDefault();
// console.log(name);
setLoading(true);
updateCategory(match.params.slug, { name }, user.token)
.then((res) => {
// console.log(res)
setLoading(false);
setName("");
toast.success(`"${res.data.name}" is updated`);
history.push("/admin/category");
})
.catch((err) => {
console.log(err);
setLoading(false);
if (err.response.status === 400) toast.error(err.response.data);
});
};
const categoryForm = () => (
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Name</label>
<input
type="text"
className="form-control"
onChange={(e) => setName(e.target.value)}
value={name}
autoFocus
required
/>
<br />
<button className="btn btn-outline-primary">Save</button>
</div>
</form>
);
return (
<div className="container-fluid">
<div className="row">
<div className="col-md-2">
<AdminNav />
</div>
<div className="col">
{loading ? (
<h4 className="text-danger">Loading..</h4>
) : (
<h4>Update category</h4>
)}
{categoryForm()}
<hr />
</div>
</div>
</div>
);
};
export default CategoryUpdate;
Still new to coding. Hope you guys can help me with this ^_^
I think your problem with match which is getting as the props. If you are having trouble with handle match props please try
useRouteMatch instaed.
import { useRouteMatch } from "react-router-dom";
function YourComponent() {
let match = useRouteMatch();
// Do whatever you want with the match...
return <div />;
}
I think this is more convinent to use.
For more examples

I am using useEffect to hit api and return some response to display it.why this error is happening

I am using useEffect to hit an api and display some data from the response.It works well in console but when i try to display the data in a component it throws an error.I am checking for the loading state though.I am showing the data after a i get a response then where does this null coming from
App.js file:
import { useState, useEffect } from 'react';
import Details from './components/Details/Details';
import Header from './components/Header/Header';
import GlobalStyle from './globalStyles';
const API_KEY = 'Private';
// const URL = `https://geo.ipify.org/api/v1?apiKey=${API_KEY}&ipAddress=${ip}`;
function App() {
const [ip, setIp] = useState('8.8.8.8');
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const res = await fetch(
`https://geo.ipify.org/api/v1?apiKey=${API_KEY}&ipAddress=${ip}`
);
const json = await res.json();
setResponse(json);
setIsLoading(false);
} catch (error) {
setError(error);
}
};
fetchData();
// return { response, error, isLoading };
}, [ip]);
return (
<>
<GlobalStyle />
<Header getIp={(q) => setIp(q)} />
<Details isLoading={isLoading} res={response} error={error} />
</>
);
}
export default App;
Header.js file:
import { useState } from 'react';
import { FaArrowRight } from 'react-icons/fa';
import React from 'react';
import { Form, FormInput, Head, HeadLine, Button } from './Header.elements';
// import { useFetch } from '../../useFetch';
const Header = ({ getIp }) => {
const [input, setInput] = useState('');
const onChange = (q) => {
setInput(q);
getIp(q);
};
return (
<>
{/* styled components */}
<Head>
<HeadLine>IP Address Tracker</HeadLine>
<Form
onSubmit={(e) => {
e.preventDefault();
onChange(input);
setInput('');
}}
>
<FormInput
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
placeholder='Search for any IP address or Domain'
/>
<Button type='submit'>
<FaArrowRight />
</Button>
</Form>
</Head>
</>
);
};
export default Header;
Details.js file:
import React from 'react';
import { Box, Location } from './Details.elements';
const Details = ({ res, error, isLoading }) => {
console.log(res);
return isLoading ? (
<div>loading...</div>
) : (
<>
<Box>
<Location>{res.location.city}</Location>
</Box>
</>
);
};
export default Details;
the error it shows:
That happens because on the first render, Details component will receive isLoading=false and res=null, so it will try to render the box so it's throwing the error.
You can initialize isLoading as true.
const [isLoading, setIsLoading] = useState(true);
Or render the Location if res has some value.
<Box>
{res && <Location>{res.location.city}</Location>}
</Box>
According to React documentation :
https://reactjs.org/docs/hooks-reference.html
By default, effects run after every completed render, but you can
choose to fire them only when certain values have changed.
So your component is rendering at least once with isLoading as false before even the API call starts.
You have two choices here:
Set isLoading initial value to true
Add optional chaining res?.location.city
https://codesandbox.io/s/stackoverflow-67755606-uuhqk

Duplicate keys in React, can't solve the issue. Encountered two children with the same key

Error:
Warning: Encountered two children with the same key, 5e0611d77833da1668feade1. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Here on this picture https://prnt.sc/qgfymk I have created 2 blogs. Delete button is working fine. I'm sending via axios to HTTP delete request using mongoose and MongoDB as my database.
But when I start to click on like button check what happens. https://prnt.sc/qgg32o
It removes my other blog post and copies one with the same name and id. The issue here is that I have different IDs but somehow when I press LIKE button it gives me another ID.
I'll give you code for both PUT request in backend and frontend for incrementLikes, I really don't know what is going on.
controllers/blogs.js (backend)
blogsRouter.put('/:id', async (request, response, next) => {
const body = request.body
const blogs = {
title:body.title,
author: body.author,
url:body.url,
likes: body.likes
}
try {
const updatedBlog = await Blog.findOneAndUpdate(request.params.id, blogs, {
new: true
})
response.json(updatedBlog.toJSON())
} catch (exception) {
next(exception)
}
})
App.js
import React, { useState, useEffect } from 'react';
import './App.css';
import Blog from './components/Blog';
import LoginForm from './components/LoginForm'
import BlogForm from './components/BlogForm'
import Notification from './components/Notification'
import loginService from './services/login';
import blogService from './services/blogs';
const App = () => {
const [blogs, setBlogs] = useState([])
const [user, setUser] = useState(null)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errorMessage, setErrorMessage] = useState(null)
// states for blog creation
const [title, setTitle] = useState('')
const [author, setAuthor] = useState('')
const [url, setUrl] = useState('')
useEffect(() => {
console.log('effect')
blogService
.getAll()
.then(response => {
console.log('promise fulfiled')
setBlogs(response.data)
})
.catch(error => {
console.log('response', error.response)
console.log('error')
})
}, [])
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
}, [])
//put request
***const incrementLike = id => {
const blog = blogs.find(b => b.id === id)
console.log('blog id', blog)
const voteLike = {...blog, likes: blog.likes + 1}
blogService
.update(id, voteLike)
.then(returnedBlog => {
setBlogs(blogs.map(blog => blog.id !== id ? blog : returnedBlog))
})
.catch(error => {
setErrorMessage(
`Blog was already removed from server`
)
setTimeout(() => {
setErrorMessage(null)
}, 5000)
})
}***
//login
const handleLogin = async (e) => {
e.preventDefault()
try {
const user = await loginService.login({username, password})
window.localStorage.setItem('loggedBlogUser', JSON.stringify(user))
setUser(user)
setUsername('')
setPassword('')
console.log('success')
} catch (exception) {
setErrorMessage('wrong credentials')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
console.log('baaad')
}
}
const deleteBlogId = (id) => {
console.log('deleted blog')
blogService
.del(id)
.then(response => {
setBlogs(blogs.filter(blog => blog.id !== id))
})
.catch(error => {
console.log(error.response);
})
}
const handleCreateBlog = async (e) => {
e.preventDefault()
const newBlogs = {
title: title,
author: author,
url: url,
date: new Date()
}
blogService
.create(newBlogs)
.then(returnedBlog => {
setBlogs(blogs.concat(returnedBlog))
setTitle('')
setAuthor('')
setUrl('')
setErrorMessage(`${author} created new blog with name ${title}`)
setTimeout(() => {
setErrorMessage(null)
}, 5000)
})
}
const loginForm = () => {
return (
<div>
<Notification message={errorMessage}/>
<div>
<LoginForm
username={username}
password={password}
handleUsernameChange={({target}) => setUsername(target.value)}
handlePasswordChange={({target}) => setPassword(target.value)}
handleSubmit={handleLogin}
/>
</div>
</div>
)
}
const handleTitleChange = (event) => {
console.log(event.target.value)
setTitle(event.target.value)
}
const blogForm = () => {
return (
<div>
<BlogForm
title={title}
author={author}
url={url}
handleTitleChange={handleTitleChange}
handleAuthorChange={({target}) => setAuthor(target.value)}
handleUrlChange={({target}) => setUrl(target.value)}
onSubmit={handleCreateBlog}
/>
</div>
)
}
const handleLogout = async () => {
window.localStorage.clear()
setUser(null)
}
const logout = () => {
return (
<div><button type="reset" onClick={handleLogout}>Logout</button></div>
)}
const blogList = () => {
return (
<div>
<h2>Blogs</h2>
<p>{user.name} logged in</p>
{logout()}
{blogs.map(blog =>
<Blog
key={blog.id}
deleteBlog={() => deleteBlogId(blog.id)}
blog={blog}
increment={() => incrementLike(blog.id)} />
)}
</div>
)
}
return (
<div className="App">
{user === null ?
loginForm() :
<div>
<Notification message={errorMessage}/>
{blogForm()}
{blogList()}
</div>
}
</div>
);
}
export default App;
Check the incrementLikes function. I think there is some kind of issue. Button for likies are in component called Blog.js
Blog.js
import React from 'react';
const Blog = ({blog, increment, deleteBlog}) => (
<div>
<button onClick={deleteBlog}>Delete</button>
{blog.title}
{blog.author}
{blog.likes}
<button onClick={increment}>Like</button>
</div>
)
export default Blog
While there shouldn't be 2 blogs with the same ID you can fix the issue at hand by replacing the key from blog.id to the index of the post like this.
<div>
<h2>Blogs</h2>
<p>{user.name} logged in</p>
{logout()}
//change
{blogs.map((blog,index) =>
<Blog
//change
key={index}
deleteBlog={() => deleteBlogId(blog.id)}
blog={blog}
increment={() => incrementLike(blog.id)} />
)}
</div>
I added //change to the lines I changed.
You can just use something like uuid for this which will generate a unique ID.
import uuid from "uuid";
<>
<h2>Blogs</h2>
<p>{user.name} logged in</p>
{logout()}
{blogs.map((blog,index) =>
<Blog
key={uuid.v4()}
deleteBlog={() => deleteBlogId(blog.id)}
blog={blog}
increment={() => incrementLike(blog.id)} />
)}
</>

Categories