I am done with the API coding and whenever I click the update button it doesn't work and doesn't call the API. When I click I want the Update button to call the API and display the data which is called by id in the respective fields so that I can re edit the stored data. I do not know where to do what please help me.
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { Table, Button } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
export default function Read() {
let navigate = useNavigate();
const [APIData, setAPIData] = useState([]);
useEffect(() => {
axios.get(`http://localhost:5000/emp`)
.then((response) => {
console.log(response.data)
setAPIData(response.data);
})
}, []);
const setData = (data) => {
let { Employee_name, Employee_id, Employee_address, Employee_post } = data;
localStorage.setItem('Employee Name', Employee_name);
localStorage.setItem('Employee ID', Employee_id);
localStorage.setItem('Employee Address', Employee_address);
localStorage.setItem('Employee Position', Employee_post)
}
// const updateData = (Employee_id) => {
// axios.get(`https://localhost:5000/emp/:Employee_id`)
// .then((data) =>{
// console.log(data)
// setAPIData(update.data);
// })
// }
const getData = () => {
axios.get(`http://localhost:5000/emp`)
.then((getData) => {
setAPIData(getData.data);
})
}
const onDelete = (Employee_id) => {
axios.delete(`http://localhost:5000/emp/:Employee_id`)
.then(() => {
getData();
})
}
const Data = () => {
navigate('/')
}
return (
<div>
<Table singleLine>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Employee Name</Table.HeaderCell>
<Table.HeaderCell>Employee ID</Table.HeaderCell>
<Table.HeaderCell>Employee Address</Table.HeaderCell>
<Table.HeaderCell>Employee position</Table.HeaderCell>
<Table.HeaderCell>Update</Table.HeaderCell>
<Table.HeaderCell>Delete</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{APIData.map((data) => {
return (
<Table.Row>
<Table.Cell>{data.Employee_name}</Table.Cell>
<Table.Cell>{data.Employee_id}</Table.Cell>
<Table.Cell>{data.Employee_address}</Table.Cell>
<Table.Cell>{data.Employee_post}</Table.Cell>
<Link to='/update'>
<Table.Cell>
<Button onClick={() => setData(data)}>Update</Button>
</Table.Cell>
</Link>
<Table.Cell>
<Button onClick={() => onDelete(data.Employee_id)}>Delete</Button>
</Table.Cell>
</Table.Row>
)
})}
</Table.Body>
</Table>
<Button onClick={Data} type='submit'>Home</Button>
</div>
)
}
As mentioned in the comments you have a Link wrapped around a button which you should remove.
// <Link to="/update">
<Table.Cell>
<Button onClick={() => setData(data)}>Update</Button>
</Table.Cell>
// </Link>
In the getData function I recommend to change the name of the variable receiving the data to response to not get confused with the function name itself, like you do in the useEffect
const getData = () => {
axios.get(`http://localhost:5000/emp`).then((response) => {
setAPIData(response.data);
});
};
In the onDelete function you are not correctly passing the Employee_id to the url, you should use ${}. The same in the commented updateData function
const onDelete = (Employee_id) => {
axios.delete(`http://localhost:5000/emp/${Employee_id}`).then(() => {
getData();
});
};
Related
I'm making a todo list app using react and firebase realtime database.
I want to get the todos ordered by date.
My Database:
And if I cant do this from firebase, is there a way to order it from the client side (react)?
My Code
Todos.js:
import { useState, useEffect } from "react";
import { signOut, onAuthStateChanged } from "firebase/auth";
import { uid } from "uid";
import { set, ref, onValue } from "firebase/database";
import { auth, db } from "../firebase";
import moment from "moment";
function Todos() {
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState("");
const navigate = useNavigate();
useEffect(() => {
auth.onAuthStateChanged((user) => {
if (user) {
onValue(ref(db, `/${auth.currentUser.uid}`), (snapshot) => {
setTodos([]);
const data = snapshot.val();
if (data !== null) {
Object.values(data).map((todo) => {
setTodos((currentTodos) => [todo, ...currentTodos]);
});
}
});
} else {
navigate("/");
}
});
}, []);
const handleSignOut = () => {
signOut(auth)
.then(() => navigate("/"))
.catch((error) => alert(error.message));
};
const addTodo = () => {
const uidd = uid();
set(ref(db, `${auth.currentUser.uid}/${uidd}`), {
task: newTodo,
uid: uidd,
createdAt: moment().format("YYYY-MM-DD k:m:s"),
});
setNewTodo("");
};
return (
<>
<Center>
<Button colorScheme="red" marginTop={5} onClick={handleSignOut}>
Logout
</Button>
</Center>
<Container
maxW="4xl"
marginTop={8}
display="flex"
alignItems="center"
justifyContent="center"
>
<Box
boxShadow="base"
rounded="lg"
padding={10}
background="white"
width="100%"
>
<Heading as="h1" size="md" textAlign="center">
Todo List App
</Heading>
<form onSubmit={(e) => e.preventDefault()}>
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
marginTop={5}
>
<Input
placeholder="New Task"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
size="lg"
width="80%"
/>
<Button
colorScheme="teal"
height={45}
rightIcon={<MdAdd />}
margin={0}
onClick={addTodo}
type="submit"
>
Add
</Button>
</Box>
</form>
{todos.map((todo, index) => {
return <Todo key={index} task={todo.task} uid={todo.uid} />;
})}
</Box>
</Container>
</>
);
}
export default Todos;
Since you are loading the TODOs for a single user, you can indeed order them by their createdAt property. To do this, use a query as shown in the documentation on ordering and filtering data:
const ref = ref(db, `/${auth.currentUser.uid}`);
const query = query(ref, orderByChild('createdAt'));
onValue(query, (snapshot) => {
...
Inside the code you'll then need to make sure to use snapshot.forEach to loop over the children in order, as calling .val() before that will return a JSON object and the properties in a JSON object are by definition not ordered:
snapshot.forEach((child) => {
console.log(child.key, child.val());
});
Im'trying to seperate api call from return/rendering but when importing teams (which contains data) shows as undefined. Is it because of the async await function?
How can I export the data and map it in the return on Teams.js?
Is there a better way to seperate the Api call from the return/rendering component?
FormTeams.js
import { useState, useEffect } from 'react';
import { Spinner } from "react-bootstrap";
import 'bootstrap/dist/css/bootstrap.min.css';
const FormTeams = () => {
const BASE_URL = process.env.REACT_APP_URL
const [isLoading, setIsLoading] = useState(true);
const [teams, setTeams] = useState([]);
useEffect(() => {
getTeams();
}, []);
const getTeams = async () => {
try {
const response = await fetch(`${BASE_URL}/teams`)
return response.json()
.then(data => {
setTeams(data)
setIsLoading(false)
})
} catch (error) {
console.log(error)
}
}
if (isLoading) {
return (<Spinner animation="border" variant="primary" />)
}
const deleteTeam = async (id) => {
try {
await fetch(`${BASE_URL}/teams/${id}`, {
method: "DELETE",
}).then(response => {
setTeams(teams.filter(team => team.id !== id))
return response.json()
})
} catch (error) {
console.log(error)
}
}
return {teams, deleteTeam}
}
export default FormTeams
Teams.js
import { Link, } from "react-router-dom";
import { Button, ButtonGroup } from "react-bootstrap";
import FormTeams from './FormTeams'
const Teams = () => {
const {teams, deleteTeam} =FormTeams()
return (
<div>
<h2 className='centered'>Clubs</h2>
<div><Link to="/Teams/add" className="link">Add New</Link></div>
<table className='teams'>
<thead>
<tr >
<th>№</th>
<th>Team</th>
<th>Actions</th>
</tr>
</thead>
<tbody >
{teams.map((team, index) => (
<tr key={team.id}>
<td>{index + 1}.</td>
<td>{team.team_name}</td>
<td>
<ButtonGroup>
<Link to={`/Teams/${team.id}`} className='link'>View</Link>
<Link to={`/Teams/edit/${team.id}`} className='edit'>Edit</Link>
<Button variant="danger" onClick={() => deleteTeam(team.id)}>Delete</Button>
</ButtonGroup>
</td>
</tr>
))}
</tbody>
</table>
<Link to={'/'} className='link'>Back To Home Page</Link>
</div>
)
}
export default Teams
You're kind of close. There are a couple things you need to change:
As a matter of convention, components (anything which renders content) should always have a PascalCase name (TeamsTable), and hooks should have a name which begins with the word use and is camelCased: useFormData.
Another matter of convention: write your effect code all in one block:
useEffect(async () => {
try { ... }
catch(error) { ... }
}, []);
Make sure your effect declares the right dependencies. In your case, you are referencing the BASE_URL variable. And even though you know the value will never change, you still need to list it as a dependency for your effect:
useEffect(..., [BASE_URL]);
Hooks should not render anything. So move your loader into the TeamsTable component.
You also need to make sure that you set isLoading back to false if there is an error. I highly recommend keeping "error" state too and updating it inside all catch blocks:
const [error, setError] = useState(null);
...
try { ... }
catch(error) {
setError(error);
setIsLoading(false);
}
...
Wrapping it all together (only showing relevant changes)
You should have a new useFormTeams hook instead of the FormTeams function you have now, and the useEffect call should be updated per my suggestions. You should also return the isLoading state:
const useFormTeams = () => {
const [error, setError] = useState(null);
...
useEffect(async () => {
try {
// do async stuff
setError(null);
setIsLoading(false);
}
catch(error) {
setError(error);
setIsLoading(false);
}
}, [BASE_URL]
...
return { teams, error, deleteTeam, isLoading };
};
And you will use your new hook as follows:
const TeamsTable = () => {
const { teams, error, deleteTeams, isLoading } = useFormTeams();
...
if (isLoading) {
return <Spinner ... />
}
if (error) {
return <div>There was an error: {error}<div>
}
...
};
Yes. This is where useHooks to the rescue!
First, separate all the logics into a hook
const useTeams = () => {
const BASE_URL = process.env.REACT_APP_URL
const [isLoading, setIsLoading] = useState(true);
const [teams, setTeams] = useState([]);
const getTeams = async () => {
try {
const response = await fetch(`${BASE_URL}/teams`)
return response.json()
.then(data => {
setTeams(data)
setIsLoading(false)
})
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getTeams();
}, []);
const deleteTeam = (id) => {
fetch(`${BASE_URL}/teams/${id}`, {
method: "DELETE",
}).then(response => {
setTeams(teams => teams.filter(team => team.id !== id))
}).catch(error => {
console.log(error)
}
}
return { teams, deleteTeam, isLoading }
}
Then use the returned values as props for the return elements
const Teams = () => {
const { teams, deleteTeam, isLoading } = useTeams()
if (isLoading) {
return <Spinner animation="border" variant="primary" />
}
return (
<div>
<h2 className='centered'>Clubs</h2>
<div><Link to="/Teams/add" className="link">Add New</Link></div>
<table className='teams'>
<thead>
<tr >
<th>№</th>
<th>Team</th>
<th>Actions</th>
</tr>
</thead>
<tbody >
{teams.map((team, index) => (
<tr key={team.id}>
<td>{index + 1}.</td>
<td>{team.team_name}</td>
<td>
<ButtonGroup>
<Link to={`/Teams/${team.id}`} className='link'>View</Link>
<Link to={`/Teams/edit/${team.id}`} className='edit'>Edit</Link>
<Button variant="danger" onClick={() => deleteTeam(team.id)}>Delete</Button>
</ButtonGroup>
</td>
</tr>
))}
</tbody>
</table>
<Link to="/" className="link">Back To Home Page</Link>
</div>
)
}
IMO, The core idea of react hooks is just to separate logics from rendering. And this is the perfect use case for your problem. The hooks provide as a blackbox of logics that may composed of multiple other hooks and can mainly used for providing data or do some behavior.
I am working on countries project.
I get information about this when the border buttons are clicked. But when I click the Back button, the previous country data does not appear. How can I fix this? Please help me!
Here is my Country Component
import React, { useEffect, useState } from 'react';
import { Link, useParams, useNavigate } from 'react-router-dom';
import Loading from './Loading';
function Country() {
const { countryCode } = useParams();
const navigate = useNavigate();
const [country, setCountry] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => { getSingleCountryData(countryCode); }, []);
const getSingleCountryData = async (countryCode) => {
setLoading(true);
try {
const country = JSON.parse(localStorage.getItem("countries")).filter(c => c.cca3 === countryCode);
setCountry(country[0]);
setLoading(false);
} catch (err) {
console.log(err);
}
}
return loading ? <Loading />
: (
<div className='country container'>
<button className='btn backBtn' onClick={() => navigate(-1)}> Back </button>
// some code
<div className="country__borders">
<h4>Border Countries:</h4>
{country.borders && country.borders.map((border, index) => {
return <Link to={`/countries/${border}`} onClick={() => getSingleCountryData(border)} key={index} className='btn'>{border}</Link>
})}
</div>
</div>
);
}
export default Country;
The useEffect hook is missing the getSingleCountryData and countryCode as dependencies. You'll want to memoize the getSingleCountryData callback so it's provided to the useEffect hook as a stable callback reference.
const getSingleCountryData = useCallback(async (countryCode) => {
setLoading(true);
try {
const country = (JSON.parse(localStorage.getItem("countries")) || [])
.filter(c => c.cca3 === countryCode);
setCountry(country[0]);
} catch (err) {
console.log(err);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
getSingleCountryData(countryCode);
}, [countryCode, getSingleCountryData]);
Since the country data is fetched and loaded when the countryCode route param updates there'll no longer be a need to trigger this fetching when the link is clicked.
{country.borders && country.borders.map((border, index) => (
<Link
key={index}
to={`/countries/${border}`}
className='btn'
>
{border}
</Link>
))}
And since the data is not longer fetched via a click handler and only referenced in the useEffect, getSingleCountryData can be moved into the useEffect hook and be removed entirely as a dependency.
const getSingleCountryData = useCallback(, []);
useEffect(() => {
const getSingleCountryData = async (countryCode) => {
setLoading(true);
try {
const country = (JSON.parse(localStorage.getItem("countries")) || [])
.filter(c => c.cca3 === countryCode);
setCountry(country[0]);
} catch (err) {
console.log(err);
} finally {
setLoading(false);
}
}
getSingleCountryData(countryCode);
}, [countryCode]);
I am building a COVID tracker Website in which I am getting data from an API.
But the data is coming in nested Object pattern through JSON.
"totalCandidates":"46",
"phases":[
{
"phase":"Phase 3",
"candidates":"5"
},
{
"phase":"Phase 2/3",
"candidates":"2"
},
{
"phase":"Phase 2b",
"candidates":"1"
}
],
To get the totalCandidates value from JSON I am just using {vaccines.totalCandidates} this is working fine.
But if I am trying to get phases which is an Array object I am getting issues. Please see the below code:
import {
MenuItem,
FormControl,
Select,
Card,
CardContent,
} from "#material-ui/core";
import "./App.css";
import StatsComp from "./StatsComp";
import Map from "./Map";
import VaccineTable from "./VaccineTable";
//import { useHistory } from "react-router-dom";
function App() {
const [countries, initCountries] = useState([]);
//To Capture the selected value in dropdown
const [country, initCountry] = useState("Universe");
const [countryInfo, initCountryInfo] = useState([]);
const [vaccineInfo, initVaccineInfo] = useState([]);
//const history = useHistory();
useEffect(() => {
fetch("https://disease.sh/v3/covid-19/all")
.then((response) => response.json())
.then((data) => {
initCountryInfo(data);
});
}, []);
//hook - use async always for api calls
useEffect(() => {
const getCntryData = async () => {
fetch("https://disease.sh/v3/covid-19/countries")
.then((response) => response.json())
.then((data) => {
const countries = data.map((country) => ({
name: country.country,
value: country.countryInfo.iso2,
flag: <img src={country.countryInfo.flag} alt="countryFlag" />,
}));
initCountries(countries);
});
};
getCntryData();
}, []);
useEffect(() => {
const getVaccineData = async () => {
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then((data) => {
initVaccineInfo(data);
});
};
getVaccineData();
}, []);
//Listener
const listenCountrySelect = async (event) => {
const countryValue = event.target.value;
initCountry(countryValue);
const url =
countryValue === "Universe"
? "https://disease.sh/v3/covid-19/all"
: `https://disease.sh/v3/covid-19/countries/${countryValue}`;
await fetch(url)
.then((response) => response.json())
.then((data) => {
initCountry(countryValue);
initCountryInfo(data);
});
};
console.log("URL :::: ", countryInfo);
console.log("Vcccinee :::", vaccineInfo);
return (
<div className="app">
<div className="HC__left">
<div className="HC__Header">
{/*Title of the Website*/}
<h1>Honest Covid</h1>
</div>
{/* Countries Dropdown for viewing information */}
<FormControl className="HC__countries__dropdown">
<Select
variant="outlined"
value={country}
onChange={listenCountrySelect}
className="HC__select"
>
{/*This will give all countries aggregate value*/}
<MenuItem value="Universe" className="HC__menuitem">
Universe
</MenuItem>
{/* Here we map through all countries and display a menuitem individually*/}
{countries.map((country) => (
<MenuItem value={country.value} className="HC__menuitem">
{" "}
{country.flag} {country.name}{" "}
</MenuItem>
))}
</Select>
</FormControl>
<div className="HC__statistics">
<StatsComp
title="Recovered"
cases={countryInfo.todayRecovered}
total={countryInfo.recovered}
/>
<StatsComp
title="Cases"
cases={countryInfo.todayCases}
total={countryInfo.cases}
/>
<StatsComp
title="Deaths"
cases={countryInfo.todayDeaths}
total={countryInfo.deaths}
/>
</div>
<Map />
</div>
{/* Here comes vaccine status and video*/}
<Card className="HC__right">
<CardContent>
<h3> Vaccine Status</h3>
<VaccineTable vaccines={vaccineInfo} />
<h3> Good Practices during COVID</h3>
</CardContent>
</Card>
</div>
);
}
export default App;
Above is my App.js in which I am fetching data and setting it in vaccineInfo state.
And then passing that object as Prop to another component VaccineTable.js.
import React from "react";
import "./VaccineTable.css";
function VaccineTable({vaccines}) {
return (
<div className="HC__vaccinetable">
{vaccines.map(({ phases }) => (
<tr>
<td>{phases}</td>
<td>
<strong>{phases}</strong>
</td>
</tr>
))}
</div>
);
}
export default VaccineTable;
But for this, I am getting an error :
" TypeError: vaccines.map is not a function "
What is the issue?
Thanks.
data is an object. you need to use data.phases to extract array and save in initVaccineInfo
useEffect(() => {
const getVaccineData = async () => {
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then((data) => {
initVaccineInfo(data.phases);
});
};
getVaccineData();
}, []);
The problem is that https://disease.sh/v3/covid-19/vaccine returns an object. You need to access the data property, which is an array. i.e.
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then(({data}) => { <---- destructure the 'data' property here, and use that
initVaccineInfo(data);
});
are you getting an array here console.log("Vcccinee :::", vaccineInfo); ? if yes then put a check inside VaccineTable.js before vaccines.map like vaccine.length && vaccines.map, since passed props are asynchronous.
If no, then put a console inside below useEffect and check what you are getting into data.
const getVaccineData = async () => {
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then((data) => {
initVaccineInfo(data.phases);
});
};
getVaccineData();
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)} />
)}
</>