I'm working on a reviews feature and I'm trying to access data from my MongoDB database and display it in my comment in react. However when I load the page its empty and nothing displays. I console logged my reponse.data and found out the array is empty and I'm unsure why.
client end
this is the component where my reviews are displayed.
const MovieInfoComponent = () => {
const [movieInfo, setMovieInfo] = useState();
const [reviewList, setReviewList] = useState([]);
const {id} = useParams()
useEffect( ()=>{
(async ()=>{
const response = await getDetails(id)
setMovieInfo(response)
//console.log(setMovieInfo);
})();
},[])
const updateReview = (newReview) => {
setReviewList(reviewList.concat(newReview))
}
useEffect(() => {
Axios.post('http://localhost:8080/api/review/getReviews', id, { headers: authHeader()})
.then(response => {
if (response.data.success) {
console.log('response.data.reviews',response.data.reviews)
// setReviewList(response.data.reviews)
} else {
alert('Error')
}
})
}, [])
return (
<Container2>
<Reviews refreshFunction={updateReview} reviewList={reviewList} movieId={movieInfo?.id}/>
</Container2>
)
}
reviews.js
import React, {useState, useRef} from 'react';
import Axios from 'axios';
import {Button, Input} from 'antd';
import authService from '../../services/auth.service'
import authHeader from '../../services/auth-header';
import FirstReview from './FirstReview';
const {TextArea} = Input;
const Reviews = (props) => {
const currentUser = authService.getCurrentUser();
const [review, setReview] = useState('');
const handleChange = (e) => {
setReview(e.target.value)
}
const onSubmit = (e) => {
e.preventDefault();
const variables = {
movieId: props.movieId,
content: review,
author: currentUser.id,
reviewId: props.reviewId,
}
Axios.post('http://localhost:8080/api/review/addReview', variables,{ headers: authHeader()})
.then(response=> {
if(response.data.success) {
setReview("")
props.refreshFunction(response.data.result)
} else {
alert('Failed to save review')
}
})
}
return (
<div>
<p>Reviews</p>
{props.reviewList && props.reviewList.map((review, index) => (
(!review.responseTo &&
<React.Fragment key={review._id}>
<FirstReview review={review} movieId={props.movieId} refreshFunction={props.refreshFunctions}/>
</React.Fragment>
)))}
<form style={{display: 'flex'}} onSubmit>
<TextArea
style={{width: '100%', borderRadius: '5px'}}
placeholder = "leave a review"
value={review}
onChange={handleChange}
/>
<Button style = {{width: '20%', height: '52px'}} onClick={onSubmit}></Button>
</form>
</div>
);
};
export default Reviews
backend
reviews routes file
const express = require('express');
const router = express.Router();
const {authJwt} = require("../middlewares");
const Review = require("../models/review.model")
router.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
router.post("/addReview", [authJwt.verifyToken], (req, res) => {
const review = new Review(req.body)
review.save((err, review) => {
if(err) return res.json({success:false, err})
Review.find({'_id': review._id})
.populate('author')
.exec((err, result) => {
if(err) return res.json({success: false, err})
return res.status(200).json({success: true, result})
})
})
})
router.post("/getReviews", [authJwt.verifyToken], (req, res) => {
Review.find({"movieId": req.body.id})
.populate('author')
.exec((err, reviews) => {
if(err) return res.status(400).send(err)
res.status(200).json({success: true, reviews})
})
})
module.exports = router ;
Related
How can i fix this error in my project? the error is ERROR TypeError: null is not an object (evaluating 'userdata.user') i am using react native expo after logging into my app and entering mainpage its giving me this error can anyone can help me to fix this error? i today send 8 hours fixing this error but i can't able to fix it can anyone can help me with that?
import { StyleSheet, Text, View, StatusBar } from 'react-native'
import React, { useEffect, useState } from 'react'
import { containerFull } from '../../CommonCss/pagecss'
import { formHead } from '../../CommonCss/formcss'
import Bottomnavbar from '../../Components/Bottomnavbar'
import TopNavbar from '../../Components/TopNavbar'
import FollowersRandomPost from '../../Components/FollowersRandomPost'
import AsyncStorage from '#react-native-async-storage/async-storage';
import * as Location from 'expo-location';
const Mainpage = ({ navigation }) => {
const [userdata, setUserdata] = React.useState(null)
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [city, setCity] = useState(null);
const [data, setData] = useState(null)
useEffect(() => {
AsyncStorage.getItem('user')
.then(data => {
// console.log('async userdata ', data)
setUserdata(JSON.parse(data))
})
.catch(err => alert(err))
}, [])
console.log('userdata ', userdata)
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
let city = await Location.reverseGeocodeAsync(location.coords);
setCity(city[0].city);
})();
}, []);
const sendCity = () => {
setCity(city);
fetch('http://192.168.1.52:3000/updateCity', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
city: city,
username: userdata.user.username
}),
})
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
};
useEffect(() => {
sendCity();
}, [])
return (
<View style={styles.container}>
<StatusBar />
<TopNavbar navigation={navigation} page={"MainPage"} />
<Bottomnavbar navigation={navigation} page={"MainPage"} />
<FollowersRandomPost />
</View>
)
}
export default Mainpage
const styles = StyleSheet.create({
container: {
width: '100%',
height: '100%',
backgroundColor: 'black',
paddingVertical: 50,
}
})
And why something city value becomes null and something new york which is correct how to fix null?
import { StyleSheet, Text, View, StatusBar } from 'react-native'
import React, { useEffect, useState } from 'react'
import { containerFull } from '../../CommonCss/pagecss'
import { formHead } from '../../CommonCss/formcss'
import Bottomnavbar from '../../Components/Bottomnavbar'
import TopNavbar from '../../Components/TopNavbar'
import FollowersRandomPost from '../../Components/FollowersRandomPost'
import AsyncStorage from '#react-native-async-storage/async-storage';
import * as Location from 'expo-location';
const Mainpage = ({ navigation }) => {
const [userdata, setUserdata] = useState(null);
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [city, setCity] = useState(null);
const [data, setData] = useState(null);
useEffect(() => {
async function getUserData() {
try {
const userDataString = await AsyncStorage.getItem('user');
const userData = JSON.parse(userDataString);
setUserdata(userData);
} catch (err) {
alert(err);
}
}
getUserData();
}, []);
useEffect(() => {
async function getLocation() {
try {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
let city = await Location.reverseGeocodeAsync(location.coords);
setCity(city[0].city);
} catch (err) {
console.error(err);
}
}
getLocation();
}, []);
useEffect(() => {
async function sendCity() {
try {
setCity(city);
const response = await fetch('http://192.168.1.52:3000/updateCity', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
city: city,
username: userdata.user.username
}),
});
const data = await response.json();
console.log('Success:', data);
} catch (err) {
console.error('Error:', err);
}
}
if (userdata) {
sendCity();
}
}, [userdata]);
console.log(city)
return (
<View style={styles.container}>
<StatusBar />
<TopNavbar navigation={navigation} page={"MainPage"} />
<Bottomnavbar navigation={navigation} page={"MainPage"} />
</View>
);
}
export default Mainpage
You need to wait AsyncStorage to finish his task before using the new data, how about using top lvl await like :
useEffect(() => {
await AsyncStorage.getItem('user')
.then(data => {
// console.log('async userdata ', data)
setUserdata(JSON.parse(data))
})
.catch(err => alert(err))
}, [])
console.log('userdata ', userdata)
for the 2nd question if you set city state inside the useEffect and with userdata dependency this will trigger the function when userdata state change, and since city initial state is null, and you can't immediatly get the new state from getLocation neither in sendCity, without rerender your component, i think you need to add one more condition before running the function, but this might have some side effect from the useEffect..
useEffect(() => {
async function sendCity() {
try {
const response = await fetch('http://192.168.1.52:3000/updateCity', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
city: city,
username: userdata.user.username
}),
});
const data = await response.json();
console.log('Success:', data);
} catch (err) {
console.error('Error:', err);
}
}
if (userdata && city) {
sendCity();
}
}, [userdata, city]);
I get this error when I try and call a function I have imported within my useEffect() hook in Dashboard.jsx. I am just trying to pull in data from database on the page load pretty much so that when user click button they can send off correct credentials to the api.
I am pulling it in from database for security reasons, so client id is not baked into the code.
I am pretty sure that I am getting this error maybe because the function is not inside a react component? although I am not 100% sure. And if that is the case I am not sure of the best way to restructure my code and get the desired output.
Code below.
mavenlinkCredentials.js
import { doc, getDoc } from "firebase/firestore";
import { useContext } from "react";
import { AppContext } from "../../context/context";
import { db } from "../../firebase";
const GetMavenlinkClientId = async () => {
const {setMavenlinkClientId} = useContext(AppContext)
const mavenlinkRef = doc(db, 'mavenlink', 'application_id');
const mavenlinkDocSnap = await getDoc(mavenlinkRef)
if(mavenlinkDocSnap.exists()){
console.log("mavenlink id: ", mavenlinkDocSnap.data());
console.log(mavenlinkDocSnap.data()['mavenlinkAccessToken'])
setMavenlinkClientId(mavenlinkDocSnap.data()['application_id'])
} else {
console.log("No doc");
}
}
export default GetMavenlinkClientId;
Dashboard.jsx
import React, { useContext, useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useNavigate } from "react-router-dom";
import { query, collection, getDocs, where, setDoc, doc, getDoc } from "firebase/firestore";
import { auth, db, logout } from "../firebase";
import { Button, Container, Grid, Paper } from "#mui/material";
import ListDividers from "../components/ListDividers";
import { AppContext } from "../context/context";
import axios from "axios";
import {SuccessSnackbar, ErrorSnackbar} from '../components/PopupSnackbar';
import GetMavenlinkClientId from "../helpers/firebase/mavenlinkCredentials";
const Dashboard = () => {
const [user, loading, error] = useAuthState(auth);
const [name, setName] = useState("");
const [ accessToken, setAccessToken ] = useState("")
const [errorAlert, setErrorAlert] = useState(false);
const [successAlert, setSuccessAlert] = useState(false);
const [mavenlinkClientId, setMavenlinkClientId] = useState("");
const {isAuthenticated} = useContext(AppContext);
const navigate = useNavigate();
const uid = user.uid
const parsedUrl = new URL(window.location.href)
const userTokenCode = parsedUrl.searchParams.get("code");
const { mavenlinkConnected, setMavenlinkConnected } = useContext(AppContext)
const { maconomyConnected, setMaconomyConnected } = useContext(AppContext)
const { bambooConnected, setBambooConnected } = useContext(AppContext)
const fetchUserName = async () => {
try {
const q = query(collection(db, "users"), where("uid", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
setName(data.name);
} catch (err) {
console.error(err);
alert("An error occured while fetching user data");
}
};
//
useEffect(() => {
if (loading) return;
if (!user) return navigate("/");
fetchUserName();
if(userTokenCode !== null){
authorizeMavenlink();
}
if(isAuthenticated){
GetMavenlinkClientId()
}
}, [user, loading]);
///put this into a page load (use effect maybe) so user does not need to press button to connect to apis
const authorizeMavenlink = () => {
console.log(uid);
const userRef = doc(db, 'users', uid);
axios({
//swap out localhost and store in variable like apitool
method: 'post',
url: 'http://localhost:5000/oauth/mavenlink?code='+userTokenCode,
data: {}
})
.then((response) => {
setAccessToken(response.data);
setDoc(userRef, { mavenlinkAccessToken: response.data}, { merge: true });
setMavenlinkConnected(true);
setSuccessAlert(true);
})
.catch((error) => {
console.log(error);
setErrorAlert(true)
});
}
//abstract out client id and pull in from db
const getMavenlinkAuthorization = () => {
window.open('https://app.mavenlink.com/oauth/authorize?client_id='+mavenlinkClientId+'&response_type=code&redirect_uri=http://localhost:3000');
window.close();
}
const authorizeBamboo = () => {
axios({
method: 'get',
url: 'http://localhost:5000/oauth/bamboo',
data: {}
})
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error);
});
// console.log('bamboo connected')
setBambooConnected(true);
}
const authorizeMaconomy = () => {
console.log("Maconomy connected")
setMaconomyConnected(true);
}
const syncAccount = async() => {
if(!mavenlinkConnected){
await getMavenlinkAuthorization()
}
if (!bambooConnected){
await authorizeBamboo();
}
if (!maconomyConnected){
await authorizeMaconomy();
}
}
const handleAlertClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setSuccessAlert(false) && setErrorAlert(false);
};
console.log(mavenlinkClientId);
return(
<>
<Container>
<div className="dashboard">
<h1>Dashboard</h1>
<Grid container spacing={2}>
<Grid item xs={12}>
<Paper style={{paddingLeft: "120px", paddingRight: "120px"}} elevation={1}>
<div className="dashboard-welcome">
<h2>Welcome {name}</h2>
<h4>{user?.email}</h4>
<hr/>
<h2>Integrations</h2>
<Button onClick={syncAccount}>
Sync Account
</Button>
{/* <Button onClick={getMavenlinkClientId}>
Bamboo Test
</Button> */}
<ListDividers/>
</div>
</Paper>
</Grid>
</Grid>
</div>
{successAlert === true ? <SuccessSnackbar open={successAlert} handleClose={handleAlertClose}/> : <></> }
{errorAlert === true ? <ErrorSnackbar open={errorAlert} handleClose={handleAlertClose}/> : <></> }
</Container>
</>
);
}
export default Dashboard;
the error is because you’re calling const {setMavenlinkClientId} = useContext(AppContext) inside the file mavenlinkCredentials.js which is not a react components.
you could maybe change the function inside mavenlinkCredentials.js to accept a setMavenlinkClientId and pass it from outside like this.
const GetMavenlinkClientId = async (setMavenlinkClientId) => {
const mavenlinkRef = doc(db, 'mavenlink', 'application_id');
const mavenlinkDocSnap = await getDoc(mavenlinkRef)
if(mavenlinkDocSnap.exists()){
console.log("mavenlink id: ", mavenlinkDocSnap.data());
console.log(mavenlinkDocSnap.data()['mavenlinkAccessToken'])
setMavenlinkClientId(mavenlinkDocSnap.data()['application_id'])
} else {
console.log("No doc");
}
}
and then you can call this function in your dashboard.js like so,
const {setMavenlinkClientId} = useContext(AppContext)
if(isAuthenticated){
GetMavenlinkClientId(setMavenlinkClientId)
}
I'm new to React and I have the issue that my UI ain't refreshing once I send a delete fetch in my React app. I tried to use a useEffect on my deleteTaskHandler but it broke my code. Any ideas how to accomplish this refresh?
This is my Task.js file, which is receiving props from a TaskList.js file, and TaskList.js file sends a component to App.js:
import React, { useState } from 'react';
import classes from './Task.module.css';
const Task = (props) => {
const [isCompleted, setIsCompleted] = useState(props.isCompleted);
const changeCompleteStatus = () => {
setIsCompleted(!isCompleted);
}
const deleteTaskHandler = async () => {
try {
const key = props.id
const response = await fetch('http://localhost:5050/delete-task/' + key, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('Something went wrong!');
};
const data = await response.json();
console.log(data);
} catch (error) {
console.log(error);
}
};
const updateTaskHandler = async () => {
const id = props.id
const taskData = {
id: id,
content: props.content,
isCompleted: !props.isCompleted,
dateCreation: props.dateCreation,
};
try {
const response = await fetch('http://localhost:5050/edit-task/' + id, {
method: 'PATCH',
body: JSON.stringify(taskData),
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Something went wrong!');
};
const data = await response.json();
console.log(data);
} catch (error) {
console.log(error);
}
};
let task;
if (props.isAllView) {
task = <div >
<input type="checkbox" onClick={updateTaskHandler} onChange={changeCompleteStatus} checked={isCompleted} />
<h2>{props.content}</h2>
<h3>{props.dateCreation}</h3>
<button onClick={deleteTaskHandler}>X</button>
</div>
} else {
task = <div >
<h2>{props.content}</h2>
<h3>{props.dateCreation}</h3>
</div>
}
return (
<li>{task}</li>
);
};
export default Task;
This is TaskList.js:
import React, { useState } from 'react';
import classes from './TaskList.module.css';
import Task from './Task';
const TaskList = (props) => {
const [taskView, setTaskView] = useState('all');
const getCompleteURL = () => {
setTaskView('complete')
props.onChangeTaskURL('http://localhost:5050/completed');
};
const getAllURL = () => {
setTaskView('all')
props.onChangeTaskURL('http://localhost:5050/');
};
const getPendingURL = () => {
setTaskView('pending')
props.onChangeTaskURL('http://localhost:5050/pending');
};
let taskList;
if (taskView != 'all') {
taskList = props.taskData.map((task) => (
<Task
key={task.id}
content={task.content}
dateCreation={task.dateCreation}
isCompleted={task.isCompleted}
isAllView={false}
/>
));
} else {
taskList = props.taskData.map((task) => (
<Task
key={task.id}
id={task.id}
content={task.content}
dateCreation={task.dateCreation}
isCompleted={task.isCompleted}
isAllView={true}
/>
));
}
return (
<div>
<ul >
{taskList}
</ul>
<button onClick={getCompleteURL}>Completed</button>
<button onClick={getAllURL}>All</button>
<button onClick={getPendingURL}>Pending</button>
</div>
);
};
export default TaskList;
This is App.js:
import React, { useState, useEffect, useCallback } from 'react';
import './App.css';
import TaskList from './components/Tasks/TaskList';
import NewTask from './components/NewTask/NewTask';
function App() {
const [tasks, setTasks] = useState([]);
const [taskURL, setTaskURL] = useState('http://localhost:5050/');
const fetchTasksHandler = useCallback(async (url) => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Something went wrong!');
}
const data = await response.json();
const loadedTasks = [];
for (const key in data) {
loadedTasks.push({
id: data[key]._id,
content: data[key].content,
isCompleted: data[key].isCompleted,
dateCreation: data[key].dateCreation
});
}
console.log(loadedTasks)
setTasks(loadedTasks);
} catch (error) {
// throw new Error('Something went wrong!');
console.log(error)
}
}, []);
useEffect(() => {
fetchTasksHandler(taskURL);
}, [fetchTasksHandler, taskURL]);
const changeTaskURL = url => {
console.log(url)
setTaskURL(url);
};
return (
<React.Fragment>
<TaskList taskData={tasks} onChangeTaskURL={changeTaskURL}></TaskList>
<NewTask></NewTask>
</React.Fragment>
);
}
export default App;
Extract deleteTaskHandler and updateTaskHandler in your App.js and pass them down to the TaskList => Task. In both methods, on successful operation update the tasks state array (for delete - filter out the deleted task, for update - swap the old task with the updated one). This way, the Task component will call the relevant handler which will update the parent tasks state, which in turn will spill down to the TaskList and Task and everything will get updated automatically.
Here is a sample. Consider it more as a pseudo code as you'll have to modify some of the parts to handle your case appropriately.
Your Task.js:
import React, { useState } from 'react';
import classes from './Task.module.css';
const Task = (props) => {
const {
updateTaskHandler,
deleteTaskHandler
} = props;
const [isCompleted, setIsCompleted] = useState(props.isCompleted);
const changeCompleteStatus = () => {
setIsCompleted(!isCompleted);
}
const updateHandler = () => {
const taskData = {
id: id,
content: props.content,
isCompleted: !props.isCompleted,
dateCreation: props.dateCreation,
};
updateTaskHandler(props.id, taskData);
};
const deleteHandler = () => {
deleteTaskHandler(props.id);
};
let task;
if (props.isAllView) {
task = <div >
<input type="checkbox" onClick={updateHandler} onChange={changeCompleteStatus} checked={isCompleted} />
<h2>{props.content}</h2>
<h3>{props.dateCreation}</h3>
<button onClick={deleteHandler}>X</button>
</div>
} else {
task = <div >
<h2>{props.content}</h2>
<h3>{props.dateCreation}</h3>
</div>
}
return (
<li>{task}</li>
);
};
export default Task;
TaskList.js:
import React, { useState } from 'react';
import classes from './TaskList.module.css';
import Task from './Task';
const TaskList = (props) => {
const [taskView, setTaskView] = useState('all');
const getCompleteURL = () => {
setTaskView('complete')
props.onChangeTaskURL('http://localhost:5050/completed');
};
const getAllURL = () => {
setTaskView('all')
props.onChangeTaskURL('http://localhost:5050/');
};
const getPendingURL = () => {
setTaskView('pending')
props.onChangeTaskURL('http://localhost:5050/pending');
};
let taskList;
if (taskView != 'all') {
taskList = props.taskData.map((task) => (
<Task
key={task.id}
content={task.content}
dateCreation={task.dateCreation}
isCompleted={task.isCompleted}
isAllView={false}
updateTaskHandler={props.updateTaskHandler}
deleteTaskHandler={props.deleteTaskHandler}
/>
));
} else {
taskList = props.taskData.map((task) => (
<Task
key={task.id}
id={task.id}
content={task.content}
dateCreation={task.dateCreation}
isCompleted={task.isCompleted}
isAllView={true}
updateTaskHandler={props.updateTaskHandler}
deleteTaskHandler={props.deleteTaskHandler}
/>
));
}
return (
<div>
<ul >
{taskList}
</ul>
<button onClick={getCompleteURL}>Completed</button>
<button onClick={getAllURL}>All</button>
<button onClick={getPendingURL}>Pending</button>
</div>
);
};
export default TaskList;
And App.js:
import React, { useState, useEffect, useCallback } from 'react';
import './App.css';
import TaskList from './components/Tasks/TaskList';
import NewTask from './components/NewTask/NewTask';
function App() {
const [tasks, setTasks] = useState([]);
const [taskURL, setTaskURL] = useState('http://localhost:5050/');
const fetchTasksHandler = useCallback(async (url) => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Something went wrong!');
}
const data = await response.json();
const loadedTasks = [];
for (const key in data) {
loadedTasks.push({
id: data[key]._id,
content: data[key].content,
isCompleted: data[key].isCompleted,
dateCreation: data[key].dateCreation
});
}
setTasks(loadedTasks);
}
catch (error) {
// throw new Error('Something went wrong!');
console.log(error)
}
}, []);
const deleteTaskHandler = async (taskID) => {
try {
const response = await fetch(`http://localhost:5050/delete-task/${taskID}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('Something went wrong!');
};
const data = await response.json();
setTasks(tasks => {
return tasks.filter(task => task.id !== taskID)
});
}
catch (error) {
console.log(error);
}
};
const updateTaskHandler = async (taskID, taskData) => {
const id = props.id
const taskData = {
id: id,
content: props.content,
isCompleted: !props.isCompleted,
dateCreation: props.dateCreation,
};
try {
const response = await fetch(`http://localhost:5050/edit-task/${taskID}`, {
method: 'PATCH',
body: JSON.stringify(taskData),
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Something went wrong!');
};
const data = await response.json();
setTasks(tasks => {
return tasks.map(task => {
if (task.id !== taskID) {
return task;
}
else {
return data; // The updated task
}
})
});
}
catch (error) {
console.log(error);
}
};
useEffect(() => {
fetchTasksHandler(taskURL);
}, [fetchTasksHandler, taskURL]);
const changeTaskURL = url => {
console.log(url)
setTaskURL(url);
};
return (
<React.Fragment>
<TaskList
taskData={tasks}
onChangeTaskURL={changeTaskURL}
deleteTaskHandler={deleteTaskHandler}
updateTaskHandler={updateTaskHandler}
/>
<NewTask />
</React.Fragment>
);
}
export default App;
You have to pass a callback that removes a task and pass it as a prop to the Task component.
In your App.js:
function deleteTask(id) {
setTasks(loadedTasks.filter(x => x.id !== id);
}
// ...
<TaskList taskData={tasks} onDelete={deleteTask}></TaskList>
In your TaskList.js:
tasks.map(task => <Task id={task.id} key={task.id} onDelete={props.deleteTask}></Task>)
In the Task.js call props.deleteTask(props.id) whenever you need to.
Note that passing a prop through two components or more is called "prop drilling" and should be avoided (by keeping the state in TaskList.js for example).
you can use
window.location.reload();
to refresh the page or
this.setState({});
to refresh the component or
const [value,setValue] = useState();
const refresh = ()=>{
setValue({});
}
to refresh the component using hooks
i hope you found this answer helpful
I am trying to call a function to fetch data from the database upon deleting a note. This is so that the array of notes can be updated to reflect the deleted note. The function where the error occurs is called deleteNote and the function I am trying to call within the .then promise is getNotes.
Below is the code in my App.js file. If someone could help me solve this I'd greatly appreciate it.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
// import HighlightOffIcon from '#material-ui/icons/HighlightOff';
import './App.css';
const App = () => {
const [note, setNote] = useState('');
const [notesList, setNotesList] = useState([]);
const getNotes = () => {
axios.get('http://localhost:8080/api')
.then((res) => setNotesList(res.data))
.catch(() => alert('Error recieving data.'));
}
useEffect(() => {
getNotes();
}, [])
const handleChange = (event) => {
const content = event.target.value;
setNote(content);
}
const handleSubmission = (event) => {
event.preventDefault();
axios({
url: 'http://localhost:8080/api/save',
method: 'POST',
data: {
content: note
}
})
.then((res) => {
console.log('Created Note');
setNote('');
getNotes();
})
.catch(() => {
console.log('Internal server error');
})
}
const deleteNote = (event) => {
const value = event.target.value;
axios({
method: 'DELETE',
url: 'http://localhost:8080/api/delete',
data: {
_id: value
}
})
.then(() => {
console.log('Note Deleted');
getNotes(); //Where the notes should be fetched upon successful deletion.
})
.catch(() => {
alert('Error deleting note.');
});
}
return (
<div className="app">
<h1>React Notes App</h1>
<form onSubmit={handleSubmission}>
<input
type="text"
placeholder="Enter note"
value={note}
onChange={handleChange}
/>
<button className="submit-button">Submit</button>
</form>
<div className="notes-list">
{notesList.map((note, index) => {
return (
<div className="note" key={index}>
<p>{note.content}</p>
<button value={note._id} className="delete-button" onClick={deleteNote}><i className="fas fa-trash-alt"></i></button>
</div>
);
})}
</div>
</div>
);
}
export default App;
I figured out the issue. When sending a request with axios, you must have a response sent back from the server in order to execute any code you may have in the promise.
example server code:
app.delete('/delete', (req, res) => {
BlogPost.delete({_id: req.body.id}, (err) => {
if (err) {
console.log(err);
} else {
console.log('Successfully deleted blog post.')
res.json({ //Must include a response to execute code within the axios promise.
msg: 'Delete request was recieved.'
});
}
});
});
I have a blog app and it worked perfectly before I have added the user login feature. After that useState hook methods freeze the application tab in the browser. I am not sure what the problem is, I am guessing it has something to do with re-rendering.
Here is my App.js
import React, { useState, useEffect } from 'react'
import Header from './components/Header'
import Filter from './components/Filter'
import AddNewBlog from './components/AddNewBlog'
import Blogs from './components/Blogs'
import blogService from './services/blogs'
import Notification from './components/Notification'
import Button from './components/Button'
import LoginForm from './components/LoginForm'
import loginService from './services/login'
import './App.css'
const App = () => {
const [ blogs, setBlogs] = useState([])
const [ newTitle, setNewTitle ] = useState('')
const [ newAuthor, setNewAuthor ] = useState('')
const [ newUrl, setNewUrl ] = useState('')
const [ newLike, setNewLike ] = useState('')
const [ blogsToShow, setBlogsToShow] = useState(blogs)
const [ message, setMessage] = useState(null)
const [ notClass, setNotClass] = useState(null)
const [ username, setUsername ] = useState('')
const [ password, setPassword ] = useState('')
const [ user, setUser ] = useState(null)
useEffect(() => {
blogService
.getAll()
.then(initialBlogs => {
setBlogs(initialBlogs)
console.log(initialBlogs)
setBlogsToShow(initialBlogs)
})
.catch(error => {
showMessage(`Error caught: ${error}`, 'error')
})
}, [])
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogappUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
})
const handleLogin = async (e) => {
e.preventDefault()
try {
const user = await loginService.login({
username, password,
})
window.localStorage.setItem('loggedBlogappUser', JSON.stringify(user))
blogService.setToken(user.token)
setUser(user)
setUsername('')
setPassword('')
} catch (error) {
showMessage('wrong credentials', 'error')
}
}
const handleLogout = () => {
console.log('logging out')
setUser(null)
window.localStorage.clear()
}
const handleAddClick = (e) => {
e.preventDefault()
if(newTitle === '') {
alert("Input Title")
}
else if (newAuthor === '') {
alert("Input Author")
}
else if (newUrl === '') {
alert("Input Url")
} else {
let newObject = {
title: newTitle,
author: newAuthor,
url: newUrl,
likes: 0
}
console.log('step0');
blogService
.create(newObject)
.then(returnedBlog => {
setBlogs(blogs.concat(returnedBlog))
setBlogsToShow(blogs.concat(returnedBlog))
resetForm()
showMessage(`Added ${newTitle}`, 'success')
})
.catch(error => {
console.log(error.response.data)
showMessage(`${error.response.data.error}`, 'error')
})
//}
}
}
const handleDeleteClick = (id, title) => {
let message = `Do you really want to delete ${title}?`
if(window.confirm(message)){
blogService
.deleteBlog(id)
.then(res => {
setBlogs(blogs.filter(b => b.id !== id))
setBlogsToShow(blogs.filter(b => b.id !== id))
})
.catch(error => {
showMessage(`${title} has already been removed from the server`, 'error')
})
}
}
const handleLikeClick = (blog) => {
const updatedObject = {
...blog,
likes: blog.likes += 1
}
blogService
.update(updatedObject)
.then(() => {
setBlogs(blogs)
showMessage(`You liked ${updatedObject.title}`, 'success')
})
}
const resetForm = () => {
setNewTitle('')
setNewAuthor('')
setNewUrl('')
setNewLike('')
document.getElementById('titleInput0').value = ''
document.getElementById('authorInput0').value = ''
document.getElementById('urlInput0').value = ''
}
const showMessage = (msg, msgClass) => {
setMessage(msg)
setNotClass(msgClass)
setTimeout(() => {
setMessage(null)
setNotClass(null)
}, 5000)
}
const handleFilterOnChange = (e) => {
const filtered = blogs.filter(blog => blog.title.toLowerCase().includes(e.target.value.toLowerCase()))
setBlogsToShow(filtered)
//setBlogs(filtered)
}
const handleAddTitleOnChange = (e) => {
console.log(e.target.value)
console.log(newTitle)
setNewTitle(e.target.value)
}
const handleAddAuthorOnChange = (e) => {
setNewAuthor(e.target.value)
}
const handleAddUrlOnChange = (e) => {
setNewUrl(e.target.value)
}
if (user === null) {
return (
<div>
<Header text={'Bloglist'} />
<Notification message={message} notClassName={notClass} />
<LoginForm
handleLogin={handleLogin}
username={username}
setUsername={setUsername}
password={password}
setPassword={setPassword}
/>
</div>
)
}
return (
<div>
<Header text={'Bloglist'} />
<Notification message={message} notClassName={notClass} />
<p>{user.name} logged in</p><Button text={"logout"} handleClick={handleLogout} />
<AddNewBlog
handleAddTitleOnChange={handleAddTitleOnChange}
handleAddAuthorOnChange={handleAddAuthorOnChange}
handleAddUrlOnChange={handleAddUrlOnChange}
handleAddClick={handleAddClick}
/>
<Filter handleFilterOnChange={handleFilterOnChange} />
<Blogs blogs={blogsToShow} handleDeleteClick={handleDeleteClick} handleLikeClick={handleLikeClick} />
</div>
)
}
export default App
Anytime I call anyone of these methods: "setNewTitle, setNewAuthor, setNewUrl, setBlogsToShow", after logging in, the tab of the browser freezes. I tried with Chrome and FireFox.
Thank you for your help.
The issue is with your useEffect
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogappUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
})
It is executed on each re-render since it has not been provided any dependency and so it send the app in an infinite loop as it itself triggers a re-render. So when you call any state updater, this useEffect is triggered causing you tab to freeze
You can make this useEffect run once on initial render by passing an empty array to it as dependency
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogappUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
}, [])