send delete request to api using fetch and useEffect hook in react - node.js

I'm trying to send a delete request to API using fetch and useEffect hook in react. I want to execute the delete request with the click of a button and pass the id of the specific JSON data that has to be deleted. I'm unable to figure out how how to pass this id to fetch function under the useEffect hook.
const Display = () => {
const [state, setState] = useState([])
let deleteJSON;
const handleDelete = (_id) => {
deleteJSON = async (id) => {
const res = await fetch(`http://localhost:8080/users/${id}`, {method: 'DELETE', mode: 'cors'})
console.log(res.status)
}
}
useEffect(() => {
(async () => {
const res = await fetch('http://localhost:8080/users', {method: 'GET', mode: 'cors'})
const jsonArr = await res.json()
setState((prevState) => {
return [...prevState, ...jsonArr]
})
})();
}, [])
return (
<div className='display'>
{
state.map((json) => {
const {name, email, phone, _id} = json
return (
<div key = {_id} >
<div>
<button onClick={(event) => { handleDelete(_id)} } className="box" >Delete</button>
<button className="box">Update</button>
</div>
<h2>{_id}</h2>
<h2>{name}</h2>
<h2>{email}</h2>
<h2>{phone}</h2>
</div>
)
})
}
</div>
)
}
As you can see I've passed the id to the handleDelete function but I can only call the deleteJSON function inside the useEffect hook. But inside the useEffect hook, I can't get access to this function.

Related

Does axios need extra config to get data from REST API?

I am trying to convert the fetch API to axios with get method.
Prior to do this, I plan to keep using 'async, await'.
And when I replaced the code below:
// before
const fetchPlanets = async () => {
const res = await fetch("http://swapi.dev/api/planets/");
return res.json();
};
// after
const fetchPlanets = async () => {
const res = await axios
.get("http://swapi.dev/api/planets/")
.then((respond) => {
respond.data;
});
};
async can be used when to address the function.
and returned const res as res.json();
Also...axios does not require to res.json as it returned as json type.
That's how I understand this so far. And with fetch API, this work flawlessly.
How the code should be to let axios work as I expected?
// Planets.js
import React from "react";
import { useQuery } from "react-query";
import Planet from "./Planet";
// import axios from "axios";
const fetchPlanets = async () => {
const res = await fetch("http://swapi.dev/api/planets/");
return res.json();
};
const Planets = () => {
const { data, status } = useQuery("planets", fetchPlanets);
console.log(data);
return (
<div>
<h2>Planets</h2>
{status === "loading" && <div>Loading data...</div>}
{status === "error" && <div>Error fetching data!</div>}
{status === "success" && (
<div>
{data.results.map((planet) => (
<Planet key={planet.name} planet={planet} />
))}
</div>
)}
</div>
);
};
export default Planets;
And Planet.js; just in case.
import React from "react";
const Planet = ({ planet }) => {
return (
<div className="card">
<h3>{planet.name}</h3>
<p>Population - {planet.population}</p>
<p>Terrain - {planet.terrain}</p>
</div>
);
};
export default Planet;
There are 2 problems in your axios code.
You should return respond.data.
You should return the whole axios response.
So this would work:
const fetchPlanets = async () => {
return await axios
.get("http://swapi.dev/api/planets/")
.then((respond) => {
return respond.data;
});
};

Activate Account with redux

it is my first time using redux, I have been trying to activate an account through mail with redux, but I didn't know how to consume the method
here is my redux activate method
export const activateAccount= createAsyncThunk('user/activateUser', async (data, { rejectWithValue }) => {
try
{
const token = window.location.href.slice(window.location.href.indexOf('?') + 1);
const config = {
method: 'put',
url: `${API_ENDPOINT}/v1/api/account/${token}/enable`,
data,
};
const payload = await axios(config);
return payload.data;
} catch (err) {
return rejectWithValue(err.response.data);
}
});
here how I try to use it
function EnableAccount()
{
const dispatch = useDispatch();
useEffect(() => {
const Activate = (entry) => {
dispatch(
activateAccount({
...entry,
}),
).then(unwrapResult);
};
Activate();
}, []);
return <Result status="success" title="Successfully Activated Account " />;}
and here im calling it in the route
<Route exact path="/ActivateAccount/:token">
<EnableAccount />
</Route>
and this one is from my html template
<a href="${API_ENDPOINT}/ActivateAccount/token=?${confirmationCode}/enable"
>I want to activate my account
</a>
and thank you.

401 error in axios post request to local server

Context
I'm building a simple web application using the MERN stack for practice. In the app, logged-in users should be able to add a new blog to the site. However, for some reason my axios post request to the backend is failing and I'm receiving a 401 error. I'm using jsonwebtoken to handle the authentication. Submitting a POST request via Insomnia works fine so I don't believe it's an issue with my endpoint. I'm running backend server locally on my machine on port 3003 and have set up a proxy so there's no issues with cors. This works fine as the blogs from the backend are displays on the frontend once a user has logged in.
I've also checked the headers and can confirm that logged-in users have a valid bearer token.
What could be causing the issue?
Frontend
I can't post any images but here's a link to the frontend view:
https://imgur.com/a/DdUlfg9
App.js
import React, { useState, useEffect } from 'react'
import Blog from './components/Blog'
import blogService from './services/blogs'
import loginService from './services/login'
import LoginForm from './components/loginForm'
import BlogForm from './components/blogForm'
const App = () => {
const [blogs, setBlogs] = useState([])
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useState(null)
const [errorMessage, setErrorMessage] = useState(null)
const [newBlog, setNewBlog] = useState({
title: '',
author: '',
url: ''
})
useEffect(() => {
blogService.getAll().then(blogs =>
setBlogs( blogs )
)
}, [])
useEffect(() => {
const loggedInUser = window.localStorage.getItem("loggedBlogUser")
if(loggedInUser){
const user = JSON.parse(loggedInUser)
setUser(user)
}
},[])
const handleLogin = async (event) => {
event.preventDefault()
try {
const user = await loginService.login({
username, password
})
window.localStorage.setItem(
'loggedBlogUser', JSON.stringify(user)
)
blogService.setToken(user.token)
setUser(user)
setUsername('')
setPassword('')
} catch (exception){
setErrorMessage('Wrong credentials')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
}
}
const handleLogout = async (event) => {
event.preventDefault()
if(user){
window.localStorage.removeItem("loggedBlogUser")
setUser(null)
}
}
const handleBlogField = (event) => {
event.preventDefault()
const {name, value} = event.target
console.log(newBlog.title)
setNewBlog(prevBlog => ({
...prevBlog,
[name] : value
}))
}
const addBlog = async (event) => {
event.preventDefault()
try {
const blog = await blogService.create(newBlog)
console.log("POST REQUEST: ",newBlog)
console.log('lets geddit')
setBlogs(blogs.concat(blog))
} catch (exception){
setErrorMessage('Uh oh, try again :[')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
}
}
if(user === null){
return(
<>
{errorMessage}
<h2>Log into application</h2>
<LoginForm handleLogin={handleLogin} setUsername={setUsername} setPassword={setPassword} username={username} password={password}/>
</>
)
}
return (
<div>
<h2>blogs</h2>
{user &&
<div>
<h3>{user.username} logged in</h3>
<button onClick={handleLogout}>Logout</button>
</div>
}
<BlogForm handleSubmission={addBlog} newBlog={newBlog} handleBlogField={setNewBlog}/>
{/* <BlogForm addBlog={addBlog} title={newBlog.title} setTitle={setTitle} setAuthor={setAuthor} author={newBlog.author} url={newBlog.url} setUrl={setUrl}/> */}
{blogs.map(blog =>
<Blog key={blog.id} blog={blog} />
)}
</div>
)
}
export default App
Blogs.js
import axios from 'axios'
const baseUrl = '/api/blogs'
let token = null
//let config
const setToken = (newToken) => {
token = `bearer ${newToken}`
}
const getAll = async () => {
const response = await axios.get(baseUrl)
return response.data
}
const create = async (newObject) => {
const config = {
headers: {
Authorization: token
}
}
const response = await axios.post(baseUrl, newObject, config)
console.log(`RESPONSE: ${newObject}`)
return response.data
}
const blogService = {
getAll, setToken, create
}
export default blogService
Have you configured CORS?, in order to accept your localhost requests?

How to make a PATCH request in ReactJS

I'm trying to send a PATCH request to my NodeJS API from my react frontend. I want a situation whereby if you click the edit button, the initial name price appears on the input for necessary editing. Then after editing, you can update it. Displaying the initial data works fine , but saving it doesn't work. I get the error: "Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function."
I've looked up the cleanup function, but couldn't make a headway.
Below is my code.
const EditUserForm = () => {
const history = useHistory();
const match = useRouteMatch();
let routeId = match.params.id;
console.log(routeId);
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [item, setItem] = useState({});
const [name, setName] = useState();
const [price, setPrice] = useState();
const handleInputChange = (e) => {
console.log(e.target.value)
const { name, value } = e.target;
setItem({ ...item, [name]: value});
};
const handleSubmit = (e) => {
e.preventDefault();
updateProduct();
history.push('/');
}
const updateProduct = () => {
fetch(`/addproducts/${routeId}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: item.name,
price: item.price
}),
})
.then((res) => res.json())
.then((result) => setItem(result))
.catch((err) => console.log('error: ', err))
}
useEffect(() => {
fetch(`/products/${routeId}`, requestOptions)
.then(res => res.json())
.then(
(result) => {
setIsLoaded(true);
setName(result.product.name);
setPrice(result.product.price);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, []);
return (
<form onSubmit={handleSubmit} >
<label>Name</label>
<input
type="text"
name="name"
defaultValue={name}
onChange={handleInputChange}
/>
<label>Price</label>
<input
type="text"
name="price"
defaultValue={price}
onChange={handleInputChange}
/>
<button type="submit">Update</button>
<button>
Cancel
</button>
</form>
)
}
export default EditUserForm
inside "handleSubmit" you are calling "history.push('/')" which produces the error, if you want to change the route then call it in .then of updateProduct
Your useEffect function is applied to each and every rendering and is not cancellable, try rewriting it like this
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(`/products/${routeId}`, {signal, ...requestOptions})
.then(res => res.json())
.then(
(result) => {
setIsLoaded(true);
setName(result.product.name);
setPrice(result.product.price);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
return controller.abort //so the fetch request can be canceled if the effect is re-executed
}, [routeId]); //only needs to rerun on route change
I am not too sure if the handles submit might cause similar problems, in which case you'd want to do something similar to this.

how to get a final snapshot for a react component after fetching datas?

I'm testing a react component UI. Within this component, a request is sent and fetch data to rerender UI. Now a snapshot before the request fetch data is produced. How to get a snapshot after the request?
// component.js
class Text extends Component {
componentDidMount() {
this.load()
}
load = () => {
const {id} = this.props
fetch('/abc').then(data => {
this.setState({data})
})
}
render() {
if(!this.state.data) return null
const {data} = this.state
return (
<div>
{data}
</div>
)
}
}
//jest
describe('test', () => {
beforeEach(() => {
fetch.mockImplementation(()=> new Promise(resolve=>resolve(4)))
});
test('base render', async () => {
const wrapper = await render(<Text/>)
expect(toJson(wrapper)).toMatchSnapshot()
})
})
//received snapshot
null
//expected snapshot
<div>
4
</div>

Resources