apidata.map is not a function : ReactJS - javascript

Can someone please tell me why i am getting this error . I tried fixing it all day, but I could not fix it. So at last i had to come at stackoverflow
This is my code : App.js
import "./App.css";
function App() {
const [inputvalue, setInputvalue] = useState(" ");
const [apidata, setApidata] = useState([]);
const [finalpoint, setFinalpoint] = useState("");
useEffect(() => {
fetch(
`https://weatherapi-com.p.rapidapi.com/forecast.json?q=+${inputvalue}&days=3`,
{
method: "GET",
headers: {
"x-rapidapi-host": "weatherapi-com.p.rapidapi.com",
"x-rapidapi-key":
"7f89bf16ebmsh9dff0f23f963d34p190691jsn264543e18108",
},
}
)
.then((response) => {
return response.json();
})
.then((data) => {
setApidata(data);
})
.catch((err) => {
console.error(err);
});
}, [finalpoint]);
const onchangeinput = (e) => {
setInputvalue(e.target.value);
};
const onsubmithandler = (e) => {
e.preventDefault();
setFinalpoint(inputvalue);
};
return (
<div className="App">
<div className="main">
<h2>Welcome To weather App </h2>
</div>
<form onSubmit={onsubmithandler}>
<input type="text" value={inputvalue} onChange={onchangeinput} />
<button type="submit">Search</button>
</form>
{apidata.map((data, i) => {
return <h1>{data.current.feelslike_c}</h1>;
})}
</div>
//Map
);
}
export default App;
This is the error I am getting :
enter image description here

Check if you are receiving undefined here:
.then((data) => {
setApidata(data);
})
and overriding the state with undefined.

What I'm seeing is the api is initially returning an error object. Also, when the proper data is returned, it comes back as an object. When setting your state, you will have to set data inside of an array (if you want to use the map method). You will also have to handle the error by doing something like this:
import { useState, useEffect } from "react";
function App() {
const [inputvalue, setInputvalue] = useState(" ");
const [apidata, setApidata] = useState([]);
const [finalpoint, setFinalpoint] = useState("");
useEffect(() => {
fetch(
`https://weatherapi-com.p.rapidapi.com/forecast.json?q=+${inputvalue}&days=3`,
{
method: "GET",
headers: {
"x-rapidapi-host": "weatherapi-com.p.rapidapi.com",
"x-rapidapi-key": "7f89bf16ebmsh9dff0f23f963d34p190691jsn264543e18108"
}
}
)
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data)
if (data.error) return null;
setApidata([data]);
})
.catch((err) => {
console.error(err);
});
}, [finalpoint]);
const onchangeinput = (e) => {
setInputvalue(e.target.value);
};
const onsubmithandler = (e) => {
e.preventDefault();
setFinalpoint(inputvalue);
};
console.log("test", apidata);
return (
<div className="App">
<div className="main">
<h2>Welcome To weather App </h2>
</div>
<form onSubmit={onsubmithandler}>
<input type="text" value={inputvalue} onChange={onchangeinput} />
<button type="submit">Search</button>
</form>
{apidata.length
? apidata.map((data, i) => {
return <h1 key={i}>{data.current.feelslike_c}</h1>;
})
: null}
</div>
//Map
);
}
see working example: https://codesandbox.io/s/eager-wind-06ywo?file=/src/App.js

Related

Struggling to filter by category properly

import React, { useEffect, useState } from "react";
import Loading from "./Loading";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
async function setCompany(companyName) {
await getData();
const newData = data.filter((info) => info.company === companyName);
setData(newData);
}
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
useEffect(() => {
getData();
}, []);
if (loading) {
return <Loading></Loading>; // simple loading screen
}
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{data.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => {
setCompany("TOMMY");
}}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => {
setCompany("BIGDROP");
}}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => {
setCompany("CUKER");
}}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
Sooo... basically I'm trying to filter the array returned by Fetch and have it display only the category I want (I called it "company instead of category in my code") depending on which button I click as shown in the "nav-buttons" div down in the code.
The first time I click on a button it works fine, but the second time it doesn't show anything as if it's filtering from an already filtered array which return no results obviously.
update these two methods with these two lines:
async function setCompany(companyName) {
const response=await getData(); //THIS ONE
const newData = response.filter((info) => info.company === companyName);
setData(newData);
}
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
return data;// And THIS ONE
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
// Get the oportunity to learn about promises, and you will save so much time. ;)
import React, { useEffect, useState } from "react";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [companyName, setCompanyName] = useState("");
async function setCompany(companyName) {
getData();
const newData = setData(newData);
}
function getData(companyName) {
setCompanyName(companyName);
fetch(url)
.then((res) => res.json())
.then((info) => {
console.log(info);
return companyName
? info.filter((info) => info.company == companyName)
: info;
})
.then((res) => {
console.log(res);
return setData(res);
})
.catch((err) => {
setLoading(false);
console.error(`ERROR ==> ${err}`);
});
}
useEffect(() => {
getData();
}, []);
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{data.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => getData("TOMMY")}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => getData("BIGDROP")}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => getData("CUKER")}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
you don't need to call the same API on each filter as it returns same data if I'm not wrong.
you can filter the data with the derived state, by storing the selected company in state i.e., on each render it calculates based on the selected company.
use the filtered data to render finally.
Here is the full e.g.
import React, { useEffect, useState } from "react";
import Loading from "./Loading";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [selectedCompany, setSelectedCompany] = useState(""); // store the company on click
const filteredData = selectedCompany ? data.filter(info=> info.company === selectedCompany) : data; // filter data based on selected company
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
useEffect(() => {
getData();
}, []);
if (loading) {
return <Loading></Loading>; // simple loading screen
}
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{filteredData.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => {
setSelectedCompany("TOMMY");
}}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => {
setSelectedCompany("BIGDROP");
}}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => {
setSelectedCompany("CUKER");
}}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
try putting a check case before filter to insure that your array isn't empty.
async function setCompany(companyName) {
await getData();
{data ?
const newData = data.filter((info) => info.company === companyName);
:
null}
setData(newData);
}
I think part of your issue is when your calling get data on button click your state isn't set before running the filter logic. I would look over your functional logic and ask yourself is this the best way to do this and am i trying to filter before or after my response.

Why do I need to submit twice for my mapped function to display the updated state after updating the array content?

I am trying to display an img for the current value in the array item, but onload, when I submit my value, the image does not display. I submit twice to display the value. When changing the value and pressing submit, the div does not update the content with new value, but repeats the old one. After submitting again, the div is updated with new value.
export default function Ingredients() {
const [form, setForm] = useState("");
const [theimg, setheimg] = useState({
name: "",
url: "",
id: "",
});
const [imgArr, setImgArr] = useState([]);
function handleChange(e) {
setForm(e.target.value);
}
function getImg() {
const options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "29a63a7413msh8378b61a2e11cf3p192e62jsn53d83f1651fe",
"X-RapidAPI-Host": "edamam-food-and-grocery-database.p.rapidapi.com",
},
};
fetch(
`https://edamam-food-and-grocery-database.p.rapidapi.com/parser?ingr=${form}`,
options
)
.then((response) => response.json())
.then((response) =>
setheimg((prevImg) => ({
...prevImg,
name: form,
url: response.parsed[0].food.image,
id: Math.random(),
}))
)
.catch((err) => console.error(err));
}
const thingsElements = imgArr.map((thing) => (
<div key={thing.id}>
<img src={thing.url} />
<p>{thing.name}</p>
</div>
));
return (
<>
<Navbar page="/" />
<Heading heading="Ingredients" info="Search by etc" />
<Form
label="Search Ingredients..."
onChange={handleChange}
value={form.value}
imgsrc={theimg}
/>
<button
onClick={() => {
getImg();
setImgArr((oldArray) => {
return [...oldArray, theimg];
});
}}
>
Search
</button>
{thingsElements}
</>
);
}
const Form = (props) => {
return (
<>
<label>
{props.label}
<input value={props.value} onChange={props.onChange} />
</label>
</>
);
};
export default Form;
In your click handler, you aren't waiting for getImg() to complete before adding theimg to the array.
Try something like this instead
const getImg = async () => {
const params = new URLSearchParams({ ingr: form });
const res = await fetch(`https://example.com/parser?${params}`, {
method: "GET",
headers: {
"X-RapidAPI-Key": "<api-key>",
"X-RapidAPI-Host": "<host>",
},
});
if (!res.ok) {
throw res;
}
const data = await response.json();
return {
name: form,
url: data.parsed[0].food.image,
id: Math.random(), // recommend Date.now() instead
};
};
const clickHandler = async () => {
try {
const newImg = await getImg(); // wait for getImg() to resolve
settheimg(newImg); // set img state
setImgArr((prev) => [...prev, newImg]); // add it to the array
} catch (err) {
console.error(err);
}
};
and in your <button>...
<button onClick={clickHandler}>Search</button>

Refactoring React class to hooks - Entity update component

I have this React component that I use to update business entities. It basically fetches by ID on componentDidMount and sends a put request when the form is submitted. I would like to refactor this to a hook based component.
Here is the code before
import React from "react";
import axios from "axios";
//Api Helper Methods
const API_HOST = "https://api.example.com";
const get = (endPoint) =>
axios
.get(`${API_HOST}/${endPoint}`)
.then((response) => response.data);
export const put = (endPoint, payload, id) =>
axios
.put(`${API_HOST}/${endPoint}/${id}`, payload)
.then((response) => response.data);
//React route (uses React Router)
const END_POINT = `users`;
class Entity extends React.Component {
state = { entity: {}, fetching: true };
getEntity = async () => {
const { id } = this.props.match.params;
this.setState({ fetching: true });
const entity = await get(`${END_POINT}/${id}`);
this.setState({ entity, fetching: false });
};
onChange = (key, value) =>
this.setState({ entity: { ...this.state.entity, [key]: value } });
componentDidMount() {
this.getEntity();
}
onSubmit = async (e) => {
e.preventDefault();
let { entity } = this.state;
let { match } = this.props;
await put(END_POINT, entity, match.params.id);
};
render() {
const { entity, fetching } = this.state;
if (fetching) {
return <p>loading...</p>;
}
return (
<form onSubmit={this.onSubmit}>
<label htmlFor="name">name</label>
<input
value={entity["name"]}
onChange={(e) => this.onChange("name", e.target.value)}
/>
<button type="submit">submit</button>
</form>
);
}
}
export default Entity;
This is what I have so far for the code after. Next step would be to extract custom hook.
const END_POINT = `users`;
export default function Entity({ match }) {
const [entity, setEntity] = useState({ name: "" });
const [fetching, setFetching] = useState( true );
const { id } = match.params;
const onChange = (key, value) => setEntity({ ...entity, [key]: value });
useEffect(() => {
const fetchEntity = async () => {
const entity = await get(`${END_POINT}/${id}`);
setEntity(entity);
setFetching(false);
};
fetchEntity();
}, [id]);
const onSubmit = async (e) => {
e.preventDefault();
await put(END_POINT, entity, id);
};
if (fetching) {
return <p>loading...</p>;
}
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">name</label>
<input
value={entity["name"]}
onChange={(e) => onChange("name", e.target.value)}
/>
<button type="submit">submit</button>
</form>
);
}
I haven't tested this but this should be close to what you want with a custom hook for your entity function.
import React, { useEffect, useState } from 'react';
const API_HOST = "https://api.example.com";
const END_POINT = `users`;
function useEntity(entityID) {
const [entity, setEntity] = useState({})
useEffect(() => {
(async () => {
await fetch(`${API_HOST}/${END_POINT}/${props.match.params}`)
.then(async res => await res.json())
.then(result => setEntity(result));
})();
}, [])
return entity
}
export default function Entity(props) {
const { id } = props.match;
const entity = useEntity(id);
const onSubmit = async () => await fetch(`${API_HOST}/${END_POINT}/${id}`, {method: 'PUT', body: entity})
if (!entity) {
return <p>loading...</p>;
}
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">name</label>
<input
value={entity["name"]}
onChange={(e) => setEntity({ ...entity, name: e.target.value})}
/>
<button type="submit">submit</button>
</form>
)
}
Thanks for the help Harben, I got it working like this.
import React, {useEffect, useState} from "react";
import axios from "axios";
//Api Helper Methods
const API_HOST = "https://api.example.com";
const get = (endPoint) =>
axios.get(`${API_HOST}/${endPoint}`).then((response) => response.data);
export const put = (endPoint, payload, id) =>
axios
.put(`${API_HOST}/${endPoint}/${id}`, payload)
.then((response) => response.data);
const END_POINT = `users`;
const useEntity = (entityId) => {
const [entity, setEntity] = useState({ name: "" });
const [fetching, setFetching] = useState(true);
useEffect(() => {
(async () => {
const entity = await get(`${END_POINT}/${entityId}`);
setEntity(entity);
setFetching(false);
})();
}, [entityId]);
return [entity, fetching, setEntity];
};
//React route (uses React Router)
export default function Entity({ match }) {
const { id } = match.params;
const [entity, fetching, setEntity] = useEntity(id);
const onChange = (key, value) => setEntity({ ...entity, [key]: value });
const onSubmit = async (e) => {
e.preventDefault();
await put(END_POINT, entity, id);
};
if (fetching) {
return <p>loading...</p>;
}
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">name</label>
<input
value={entity["name"]}
onChange={(e) => onChange("name", e.target.value)}
/>
<button type="submit">submit</button>
</form>
);
}

SyntaxError Unexpected token < in JSON at position 0 in react.js API navigation

rnmservice.js
export function getrnm({ url }) {
return new Promise((resolve, reject) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data);
});
});
}
export async function getAllrnm(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data);
});
});
}
app.js
import React, { useState, useEffect } from "react";
import Navbar from "./components/Navbar";
import Card from "./components/Card/Card";
import { getrnm, getAllrnm } from "./services/rmservice";
function App() {
const [rnmData, setRnmData] = useState([]);
const [nextUrl, setNextUrl] = useState("");
const [prevUrl, setPrevUrl] = useState("");
const [loading, setLoading] = useState(true);
const initialURL = "https://rickandmortyapi.com/api/episode/";
useEffect(() => {
async function fetchData() {
let response = await getAllrnm(initialURL);
setNextUrl(response.next);
setPrevUrl(response.previous);
await loadRnm(response.results);
setLoading(false);
}
fetchData();
}, []);
const next = async () => {
setLoading(true);
let data = await getAllrnm(nextUrl);
await loadRnm(data.results);
setNextUrl(data.next);
setPrevUrl(data.previous);
setLoading(false);
};
const prev = async () => {
if (!prevUrl) return;
setLoading(true);
let data = await getAllrnm(prevUrl);
await loadRnm(data.results);
setNextUrl(data.next);
setPrevUrl(data.previous);
setLoading(false);
};
const loadRnm = async data => {
let _rnmData = await Promise.all(
data.map(async rnm => {
let rnmRecord = await getrnm(rnm);
return rnmRecord;
})
);
setRnmData(_rnmData);
};
return (
<>
<Navbar />
<div>
{loading ? (
<h1 style={{ textAlign: "center" }}>Loading...</h1>
) : (
<>
<div className="btn">
<button onClick={prev}>Prev</button>
<button onClick={next}>Next</button>
</div>
<div className="grid-container">
{rnmData.map((rnm, i) => {
return <Card key={i} ricmor={rnm} />;
})}
</div>
<div className="btn">
<button onClick={prev}>Prev</button>
<button onClick={next}>Next</button>
</div>
</>
)}
</div>
</>
);
}
export default App;
Here My sandbox link is https://codesandbox.io/s/musing-thunder-frsk6 . I'm trying to fetch data and to do navigation using the API of ricky and morty (https://rickandmortyapi.com/api/episode/) but when I click next button I'm getting error as "SyntaxError Unexpected token < in JSON at position 0"
You just replace your useEffect with this
useEffect(() => {
async function fetchData() {
let response = await getAllrnm(initialURL);
console.log(response.info);
setNextUrl(response.info.next);
setPrevUrl(response.info.previous);
setPages(response.info.pages);
await loadRnm(response.results);
setLoading(false);
}
fetchData();
}, []);
you are directly accessing next and previous. access response.info.next like this because all next/previous coming in info.

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