PATCH request seems like a step behind - reactjs

Hey folks really hope someone can help me here. I'm successfully updating my object in my mongo cluster, it updates but it does not render that update straight away to the browser. It will only update after a reload or when I run my update function again, it doesn't fetch that update straight away and I can't understand why. Does anyone have any suggestions?
I'm using context and reducer.
PlantDetails
import { usePlantsContext } from "../hooks/usePlantsContext";
import formatDistanceToNow from "date-fns/formatDistanceToNow";
import { useState } from "react";
import CalendarComponent from "./CalendarComponent";
const PlantDetails = ({ plant }) => {
const [watered, setWatered] = useState(false)
const [newWaterDate, setNewWaterDate] = useState("")
const { dispatch } = usePlantsContext();
const handleClick = async () => {
const response = await fetch("/api/plants/" + plant._id, {
method: "DELETE",
});
const json = await response.json();
if (response.ok) {
dispatch({ type: "DELETE_PLANT", payload: json });
}
};
const updatePlant = async (e) => {
e.preventDefault()
plant.nextWaterDate = newWaterDate
const response = await fetch("api/plants/" + plant._id, {
method: "PATCH",
body: JSON.stringify(plant),
headers: {
'Content-Type': 'application/json'
}
})
const json = await response.json()
if(response.ok) {
dispatch({ type: "UPDATE_PLANT", payload: json })
}
console.log('updated')
setWatered(false)
}
return (
<div className="plant-details">
<h4>{plant.plantName}</h4>
<p>{plant.quickInfo}</p>
<p>
{formatDistanceToNow(new Date(plant.createdAt), { addSuffix: true })}
</p>
<span onClick={handleClick}>delete</span>
<div>
<p>next water date: {plant.nextWaterDate}</p>
<input onChange={(e) => setNewWaterDate(e.target.value)}/>
<button onClick={updatePlant}>update</button>
<input value={watered} type="checkbox" id="toWater" onChange={() => setWatered(true)}/>
<label for="toWater">watered</label>
{watered && <CalendarComponent updatePlant={updatePlant} setNextWaterDate={setNewWaterDate}/>}
</div>
</div>
);
};
export default PlantDetails;
Context which wraps my
import { createContext, useReducer } from 'react'
export const PlantsContext = createContext()
export const plantsReducer = (state, action) => {
switch(action.type) {
case 'SET_PLANTS':
return {
plants: action.payload
}
case 'CREATE_PLANT':
return {
plants: [action.payload, ...state.plants]
}
case 'DELETE_PLANT':
return {
plants: state.plants.filter((p) => p._id !== action.payload._id)
}
case 'UPDATE_PLANT':
return {
plants: state.plants.map((p) => p._id === action.payload._id ? action.payload : p )
}
default:
return state
}
}
export const PlantsContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(plantsReducer, {
plants: null
})
return (
<PlantsContext.Provider value={{...state, dispatch}}>
{ children }
</PlantsContext.Provider>
)
}
My plantController (update)
const updatePlant = async (req, res) => {
const { id } = req.params
if(!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({ error: "No plant" })
}
const plant = await Plant.findByIdAndUpdate({ _id: id }, {
...req.body
})
if (!plant) {
return res.status(400).json({ error: "No plant" })
}
res.status(200)
.json(plant)
}
Home component
import { useEffect } from "react";
import PlantDetails from "../components/PlantDetails";
import PlantForm from "../components/PlantForm";
import CalendarComponent from "../components/CalendarComponent";
import { usePlantsContext } from "../hooks/usePlantsContext";
const Home = () => {
const { plants, dispatch } = usePlantsContext();
useEffect(() => {
const fetchPlants = async () => {
console.log("called");
// ONLY FOR DEVELOPMENT!
const response = await fetch("/api/plants");
const json = await response.json();
if (response.ok) {
dispatch({ type: "SET_PLANTS", payload: json });
}
};
fetchPlants();
}, [dispatch]);
return (
<div className="home">
<div className="plants">
{plants &&
plants.map((plant) => <PlantDetails key={plant._id} plant={plant} />)}
</div>
<PlantForm />
</div>
);
};
export default Home;
Any help would be greatly appreciated.

My patch requests were going through smoothly but my state would not update until I reloaded my page. It was not returning the document after the update was applied.
https://mongoosejs.com/docs/tutorials/findoneandupdate.html#:~:text=%3B%20//%2059-,You,-should%20set%20the

Related

Update my state after PATCH request - useReducer, context

I'm successfully updating my plant object to my cluster, but it takes a page reload in order for me to get that updated data. I'm assuming that I may need a useEffect to call my fetch again but I'm unsure how I would do that after my PATCH request.
Does anyone have any suggestions to how I would fetch my updated data after I've updated.
Context
import { createContext, useReducer } from 'react'
export const PlantsContext = createContext()
export const plantsReducer = (state, action) => {
switch(action.type) {
case 'SET_PLANTS':
return {
plants: action.payload
}
case 'CREATE_PLANT':
return {
plants: [action.payload, ...state.plants]
}
case 'DELETE_PLANT':
return {
plants: state.plants.filter((p) => p._id !== action.payload._id)
}
case 'UPDATE_PLANT':
return {
plants: state.plants.map((p) => p._id === action.payload._id ? action.payload : p)
}
default:
return state
}
}
export const PlantsContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(plantsReducer, {
plants: null
})
return (
<PlantsContext.Provider value={{...state, dispatch}}>
{ children }
</PlantsContext.Provider>
)
}
My 'update' function inside PlantDetails component, setting a new water date
const updatePlant = async (e) => {
e.preventDefault()
plant.nextWaterDate = newWaterDate
const response = await fetch("api/plants/" + plant._id, {
method: "PATCH",
body: JSON.stringify(plant),
headers: {
'Content-Type': 'application/json'
}
})
const json = await response.json()
if(response.ok) {
dispatch({ type: "UPDATE_PLANT", payload: json })
}
}
My Home component where that update should render through after PATCH request
const Home = () => {
const { plants, dispatch } = usePlantsContext();
useEffect(() => {
const fetchPlants = async () => {
console.log("called");
// ONLY FOR DEVELOPMENT!
const response = await fetch("/api/plants");
const json = await response.json();
if (response.ok) {
dispatch({ type: "SET_PLANTS", payload: json });
}
};
fetchPlants();
}, [dispatch]);
return (
<div className="home">
<div className="plants">
{plants &&
plants.map((plant) => <PlantDetails key={plant._id} plant={plant} />)}
</div>
<PlantForm />
</div>
);
};
export default Home;
usePlantContext
import { PlantsContext } from "../context/PlantContext";
import { useContext } from "react";
export const usePlantsContext = () => {
const context = useContext(PlantsContext)
if(!context) {
throw Error('usePlantsContext must be used inside an PlantsContext Provider')
}
return context
}
Complete PlantsDetails Component
import { usePlantsContext } from "../hooks/usePlantsContext";
import formatDistanceToNow from "date-fns/formatDistanceToNow";
import { useState } from "react";
import CalendarComponent from "./CalendarComponent";
const PlantDetails = ({ plant }) => {
const [watered, setWatered] = useState(false)
const [newWaterDate, setNewWaterDate] = useState("")
const { dispatch } = usePlantsContext();
const handleClick = async () => {
const response = await fetch("/api/plants/" + plant._id, {
method: "DELETE",
});
const json = await response.json();
if (response.ok) {
dispatch({ type: "DELETE_PLANT", payload: json });
}
};
const updatePlant = async (e) => {
e.preventDefault()
plant.nextWaterDate = newWaterDate
const response = await fetch("api/plants/" + plant._id, {
method: "PATCH",
body: JSON.stringify(plant),
headers: {
'Content-Type': 'application/json'
}
})
const json = await response.json()
if(response.ok) {
dispatch({ type: "UPDATE_PLANT", payload: json })
}
// setWatered(false)
}
return (
<div className="plant-details">
<h4>{plant.plantName}</h4>
<p>{plant.quickInfo}</p>
<p>
{formatDistanceToNow(new Date(plant.createdAt), { addSuffix: true })}
</p>
<span onClick={handleClick}>delete</span>
<div>
<p>next water date: {plant.nextWaterDate}</p>
{/* <input type="checkbox" id="toWater" onChange={() => setWatered(true)}/> */}
<label value={watered} for="toWater">watered</label>
<CalendarComponent setNextWaterDate={setNewWaterDate}/>
</div>
<button onClick={updatePlant}>update</button>
</div>
);
};
export default PlantDetails;
Plant Controller
const updatePlant = async (req, res) => {
const { id } = req.params
if(!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({ error: "No plant" })
}
const plant = await Plant.findByIdAndUpdate({ _id: id }, {
...req.body
})
if (!plant) {
return res.status(400).json({ error: "No plant" })
}
res.status(200).json(plant)
}
Thank you for looking at my question, would appreciate any suggestion.

I am trying to add a recipe, which was never added before. I tried to call the favourites array and check if there is an id like that

I am trying to add a recipe to my favourites list but only if it's not in the list already.
I tried to fetch the favourites array and check for the same id, but it's not working. It doesn't take the array. When I console.log the array there is nothing. Please any help.
Here is the RecipeModel logic
import "./RecipeModel.css";
import { Link, NavLink, useNavigate, useParams } from 'react-router-dom';
import React, { useLayoutEffect, useState,useEffect, useContext } from 'react';
import * as userService from '../../../services/userService';
import { AuthContext } from "../../../contexts/AuthContext";
import * as likeService from '../../../services/likeService';
import useRecipeState from "../../EditRecipe/RecipeState";
const RecipeModel = ({recipe}) => {
const history = useNavigate();
const {user} = useContext(AuthContext);
const {recipeId} = useParams();
const [likes,setLikes] = useRecipeState(recipeId);
const [favourites,setFavourites] = useState([]);
useEffect(() => {
likeService.getLikes(recipe._id)
.then(likes => {
setLikes(state => ({...state, likes}))
})
}, []);
useEffect(() => {
userService.yourFavourites(user._id)
.then(result => {
setFavourites(result);
})
},[]);
const valid = !(favourites.some(i => i._id === recipe._id));
if(valid) {
const HandleFavourite = (e) => {
e.preventDefault();
console.log(`Recipe ${recipe._id}`);
const name = recipe.name;
const time = recipe.time;
const imageUrl = recipe.imageUrl;
const ingredients = recipe.ingredients;
const instructions = recipe.instructions;
userService.addFavourite({
name,
time,
imageUrl,
ingredients,
instructions
},user.accessToken)
.then(result => {
console.log(result);
console.log(index);
console.log(user._id);
history('/favourites');
})
}
}
const likeButtonClick = () => {
if (user._id === recipe._ownerId) {
return;
}
if (recipel.likes.includes(user._id)) {
return;
}
likeService.like(user._id, recipeId)
.then(() => {
setLikes(state => ({...state, likes: [...state.likes, user._id]}));
console.log('liked');
});
};
return (
<article className="articles">
<img className="img2" src={recipe.imageUrl}/>
<h1>{recipe.name}</h1>
<p className="cut-text">{recipe.instructions}</p>
<div className="btns1">
<Link smooth= "true" className="btnd" to={`/recipe-details/recipe-number:${recipe._id}`}>Details</Link>
<button className="like" onClick={likeButtonClick} > <i className="fas fa-solid fa-thumbs-up"></i> {likes.likes?.length || 0}</button>
<button className="favour" onClick={HandleFavourite} ><i className="fas fa-solid fa-heart-circle-plus"></i></button>
</div>
</article>
);
};
export default RecipeModel;
Thats the userService logic :
export const addFavourite = async (recipeData,token) => {
let response = await fetch(`${baseUrl}/favourites`, {
method: 'POST',
headers: {
'content-type': 'application/json',
'X-Authorization': token,
},
body: JSON.stringify(recipeData)
});
let result = await response.json();
return result;
};
export const getAllFavourites = async () => {
let response = await fetch(`${baseUrl}/favourites`)
let recipes = await response.json();
let result = Object.values(recipes);
return result;
}
export const removeFavourite = (recipeId, token) => {
return fetch(`${baseUrl}/favourites/${recipeId}`, {
method: 'DELETE',
headers: {
'X-Authorization': token,
},
}).then(res => res.json());
};
export const getOneFav = (recipeId) => {
return fetch(`${baseUrl}/favourites/${recipeId}`)
.then(res => res.json())
};
export const yourFavourites = (ownerId) => {
let query = encodeURIComponent(`_ownerId="${ownerId}"`);
return request.get(`${baseUrl}/favourites?where=${query}`);
};

Flickr clone: Adding a picture to an album through redux-react but page only re-renders on refresh

I'm trying to add a feature to add a picture to an album. They are connected through the album's id and picture's album_id. I can click on the album button and the picture will be added to that album but the page does not re-render. Also the handleAlbumID function runs before I even hit submit so I think that is where I am really confused...
Here is the PicturePage component:
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import { getPictureThunk } from '../../store/picture';
import CommentsPage from '../comments/Comments.js';
import CreateComment from '../comments/PostComment';
import AddToAlbum from '../AddtoAlbum';
import { getAllAlbumsThunk } from '../../store/album';
import './pictures.css'
const PicturePage = () => {
const dispatch = useDispatch()
const { id } = useParams();
// const sessionUser = useSelector(state => state.session.user)
const pictureObj = useSelector(state => state.pictureReducer)
let picture = pictureObj.picture;
let wholeDate = pictureObj.picture?.created_at
let newDate = new Date(wholeDate).toUTCString();
let finalDate = newDate.split(' ').slice(0, 4).join(' ');
useEffect(() => {
dispatch(getPictureThunk(+id))
}, [dispatch])
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchData() {
const response = await fetch('/api/users/');
const responseData = await response.json();
setUsers(responseData.users);
}
fetchData();
}, []);
const picUser = users.find(user => user?.id === pictureObj.picture?.user_id )
useEffect(() => {
dispatch(getAllAlbumsThunk())
}, [dispatch])
return (
<>
<div>
<h1>{pictureObj.picture?.content}</h1>
<div className='pictureContainer'>
<div>
<div>Uploaded by {picUser?.username} on {finalDate}</div>
<br></br>
<img alt=''src={`${pictureObj.picture?.image}`}></img>
<CommentsPage/>
<CreateComment/>
</div>
<span>
<AddToAlbum picture={picture}/>
</span>
</div>
</div>
</>
)
}
export default PicturePage;
Here is the AddtoAlbum component I created:
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getAllAlbumsThunk } from '../store/album'
import { editPictureThunk } from '../store/picture';
const AddToAlbum = ({picture}) => {
const dispatch = useDispatch()
console.log('alPic', picture)
const sessionUser = useSelector(state => state.session.user)
const albumObj = useSelector(state => state.albumReducer)
const [albumID, setAlbumID] = useState(picture?.album_id)
let albums = Object.values(albumObj)
let usersAlbums = albums.filter(album => album?.user_id === sessionUser?.id)
// console.log("user", usersAlbums)
let specificAlbum = usersAlbums.find(album => album?.id === picture?.album_id)
let handleAlbumId = async (e) => {
e.preventDefault();
// console.log('e', e.target)
let updatedPicture;
console.log('alID', albumID)
updatedPicture = {
user_id: sessionUser?.id,
album_id: albumID,
content: picture?.content,
image: picture?.image
}
// }
console.log('pic', picture?.id)
return dispatch(editPictureThunk(+picture?.id, updatedPicture))
}
useEffect(() => {
dispatch(getAllAlbumsThunk())
}, [dispatch])
return (
<>
<h1>Album</h1>
<div className='albums-container'>
{picture?.album_id === null && (
<div>This picture currently doesn't belong to an album</div>
)}
{picture?.album_id && (
<div>In {specificAlbum?.title}</div>
)}
<div>
<form onSubmit={handleAlbumId}>
<ul>
{usersAlbums.map(({ id, title}) => (
<li className='album-container' key={id}>
<button
value={albumID}
onClick={() => setAlbumID(id)}
>{title}
</button>
</li>
))}
</ul>
<button type='submit'>Submit</button>
</form>
</div>
</div>
</>
)
}
export default AddToAlbum;
Here is my update route in Python:
#picture_routes.route('/<int:id>', methods=['PUT'])
def update(id):
foundPic = Picture.query.get(id)
user_id = request.json['user_id']
album_id = request.json['album_id']
content = request.json['content']
image = request.json['image']
if len(content) > 20:
return {'errors': "Title must be less than 20 characters"}
foundPic.user_id = user_id
foundPic.album_id = album_id
foundPic.content = content
foundPic.image = image
db.session.add(foundPic)
db.session.commit()
return foundPic.to_dict()
Here is my store:
export const editPicture = (id, updatedPic) => {
return {
type: EDIT_PICTURE,
id,
updatedPic
}
}
//Thunks
export const getAllPicturesThunk = () => async dispatch => {
const res = await fetch('/api/pictures')
if (res.ok) {
const pictures_obj = await res.json()
dispatch(getAllPictures(pictures_obj))
}
}
export const getPictureThunk = (id) => async dispatch => {
const res = await fetch(`/api/pictures/${id}`)
if (res.ok) {
const data = await res.json()
dispatch(getPicture(data))
}
}
export const postPictureThunk = (data) => async dispatch => {
const res = await fetch('/api/pictures/new', {
method: "POST",
body: data
})
if (res.ok) {
const newPic = await res.json()
if (newPic.errors) {
return newPic.errors
}
dispatch(postPicture(newPic))
// return newPic
}
// else {
// const error = await res.json()
// return error
// }
}
export const deletePictureThunk = (id) => async dispatch => {
const res = await fetch(`/api/pictures/${id}`,{
method: 'DELETE'
})
if (res.ok) {
const delObj = await res.json()
// console.log('deletedpic', delObj.id)
dispatch(deletePicture(delObj.id))
}
}
export const editPictureThunk = (id, data) => async dispatch => {
const res = await fetch(`/api/pictures/${id}`, {
method: 'PUT',
headers: {'Content-Type': "application/json"},
body: JSON.stringify(data)
})
if (res.ok) {
const updatedPic = await res.json()
// console.log('updatedPic', updatedPic.updated_pic)
if (updatedPic.errors) {
return updatedPic.errors
}
dispatch(editPicture(id, updatedPic))
return updatedPic
}
}
const initialState = {};
const pictureReducer = (state = initialState, action) => {
let newState;
switch(action.type) {
case GET_PICTURES:
newState = { ...state };
// action.payload.pictures?.forEach((picture) => newState[picture.id] = picture)
// return newState;
return action.payload
case SINGLE_PICTURE:
newState = {...state}
return action.payload
case POST_PICTURE:
newState = {...state}
newState[action.payload.id] = action.payload
// console.log('here', action.payload.picture)
return newState;
case DELETE_PICTURE:
newState = {...state}
delete newState[action.payload]
return newState
case EDIT_PICTURE:
newState = {...state}
newState[action.updatedPic.id] = action.updatedPic
return newState
default:
return state
}
}

Unable to fetch data from api in component

I am working on this project in React JS where I fetch data from this API URL for my frontend development.
I have made my custom hooks to fetch the data into several files following this medium article as follows:
useApiResult.js
import { useState, useEffect } from "react";
export const useApiResult = (request) => {
const [results, setResults] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetch(request)
.then(async (response) => {
if (response.ok) {
setResults(await response.json());
setError(null);
} else {
setError(await response.text())
}
})
.catch((err) => {
setError(err.message);
});
}, [request]);
return [results, error];
};
useImages.js
import { useMemo } from "react";
import { useApiResult } from "./useApiResult";
const BASE_URL = "http://api.vidyarajkumari.com";
const createUrl = (base, path) => `${base}${path}`;
const getImages = () => [
createUrl(BASE_URL, "/images/"),
{
method: "GET",
}
];
export const useImages = () => {
const request = useMemo(() => getImages(), []);
return useApiResult(request);
}
React component: Images.js
import React from "react";
import { useImages } from "../../hooks/useImages";
export default function Images() {
const [images, error] = useImages();
//console.log(images);
//console.log(error);
return (
<>
<div className="row">
{
images.map((item, index) => {
<div key={index} className="col-md-4 animate-box">
...
// Rest of code goes here
}
}
</>
</>
)
}
The problem is that I am unable to get the data in the Images.js component from the useImages hook. The console.log values of images return null. This has been bugging me for a while now and I would greatly appreciate a solution to this. What am I doing wrong here and how can I work around this?
P.S. The API Url is live; so feel free to reference it. Thank you for your time.
I Have a better way to do this using useReducer and custom hook, check this:
By the way, I think your API URL has some problems! (I added input for fetching another URL for test)
const IMAGE_URL = "http://api.vidyarajkumari.com/images/";
const initialState = { loading: true };
function fetchReducer(state, action) {
switch (action.type) {
case "fetch":
return {
...state,
error: undefined,
loading: true,
};
case "data":
return {
...state,
data: action.data,
loading: false,
};
case "error":
return {
...state,
error: "Error fetching data. Try again",
loading: false,
};
default:
return state;
}
}
function useFetch(url) {
const [state, dispatch] = React.useReducer(fetchReducer, initialState);
React.useEffect(() => {
dispatch({ type: "fetch" });
fetch(url, {
headers: {
accept: "application/json",
},
})
.then((res) => res.json())
.then((data) => dispatch({ type: "data", data }))
.catch((e) => {
console.warn(e.message);
dispatch({ type: "error" });
});
}, [url]);
return {
loading: state.loading,
data: state.data,
error: state.error,
};
}
function FetchComponent({url}) {
const { loading, data, error } = useFetch(url);
console.log(data);
if (loading) {
return <p>Fetching {url}...</p>;
}
if (error) {
return <p>{error}</p>
}
return <div>{JSON.stringify(data)}</div>
}
const App = () => {
const [url, setUlr] = React.useState(IMAGE_URL)
const inputEl = React.useRef(null);
const changeUrl = () => setUlr(inputEl.current.value)
return (
<React.Fragment>
<input defaultValue="https://icanhazdadjoke.com/" ref={inputEl} type="text" />
<button onClick={changeUrl}>Fetch</button>
{url && <FetchComponent url={url}/>}
</React.Fragment>
)
}
ReactDOM.render(<App/>, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Give results and error also, in the dependency array, So that component get render when result is updated.
import { useState, useEffect } from "react";
export const useApiResult = (request) => {
const [results, setResults] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetch(request)
.then(async (response) => {
if (response.ok) {
setResults(await response.json());
setError(null);
} else {
setError(await response.text())
}
})
.catch((err) => {
setError(err.message);
});
}, [request, results, error]);
return [results, error];
};

Trying to setState with React Hooks, using axios.. Not getting data

I'm using an axios call to a database to get "about me" data, for client to update. DB is connected properly, as I am able to log in just fine, I've isolated this issue pretty well to my GET request.
My context provider file:
import React, { useState } from 'react'
import axios from 'axios'
export const UserContext = React.createContext()
const userAxios = axios.create()
userAxios.interceptors.request.use((config) => {
const token = localStorage.getItem("token")
config.headers.Authorization = `Bearer ${token}`
return config
})
const UserProvider = (props) => {
const initState = {
user: JSON.parse(localStorage.getItem("user")) || {},
token: localStorage.getItem("token") || "",
authErrMsg: ""
}
const [userState, setUserState] = useState(initState)
const [dataState, setDataState] = useState({
bioData: []
})
const login = credentials => {
axios.post("/auth/login", credentials)
.then(res => {
const { user, token } = res.data
localStorage.setItem("user", JSON.stringify(user))
localStorage.setItem("token", token)
setUserState(res.data)
})
.catch(err => handleAuthErr(err.response.data.errMsg))
}
const handleAuthErr = errMsg => {
setUserState(prevUserState => ({
...prevUserState,
authErrMsg: errMsg
}))
}
const logout = () => {
localStorage.removeItem("token")
localStorage.removeItem("user")
setUserState({
user: {},
token: "",
authErrMsg: ""
})
}
const getData = () => {
axios.get('/info/bio')
.then(res => {
setDataState(prevData => ({
...prevData,
bioData: res.data
}))
})
.catch(err => {
console.log(err)
})
}
const deleteBio = (id) => {
userAxios.delete(`/api/bio/${id}`)
.then(res => {
setDataState(prevData => ({
...prevData,
bioData: dataState.bioData.filter(bio => bio._id !== id)
}))
})
.catch(err => console.log(err.response.data.errMsg))
}
const addBio = (newText) => {
const newBio = {
bioText: newText
}
userAxios.post('/api/bio', newBio)
.then(res => {
getData()
})
.catch(err => console.log(err))
}
const editBio = (update, id) => {
const updatedBio = {
bioText: update
}
userAxios.put(`/api/bio/${id}`, updatedBio)
.then(res => {
console.log(res.data, 'edited')
getData()
})
.catch(err => console.log(err))
}
return (
<UserContext.Provider
value={{
user: userState.user,
token: userState.token,
authErrMsg: userState.authErrMsg,
login: login,
logout: logout,
getData: getData,
dataState: dataState,
editBio: editBio,
deleteBio: deleteBio,
addBio: addBio
}}>
{props.children}
</UserContext.Provider>
)
}
export default UserProvider
Here's my Bio component. The loading effect never changes because for some reason, no "bioData" is saving, in the provider. Tested it with that little button/handleClick and coming up an empty array.
import React, {useContext, useState, useEffect} from 'react'
import { UserContext } from './context/userProvider'
const Bio = () => {
const { token, editBio, dataState: {bioData} } = useContext(UserContext)
const [loader, setLoader] = useState('Loading')
useEffect(() => {
if(bioData[0]?._id === undefined){
setLoader('Loading')
}else {
setLoader(bioData[0]?._id)
}
})
// let initText = bioData[0].bioText
const [bioText, setBioText] = useState("initText")
const handleChange = (e) => {
setBioText(e.target.value)
}
const handleUpdate = () => {
editBio(bioText, bioData[0]._id)
alert`Bio successfully updated. :)`
}
const handleClick = () => {
console.log(bioData)
}
return (
<div className='bio'>
<h1>About Me</h1>
<div className='bio-content'>
{loader === 'Loading' ?
<div>
<p>Loading...</p>
<button onClick={handleClick}>thing</button>
</div>
:
<>
{token ?
<div className="editBio">
<p>edit mee</p>
</div>
:
<h4>{bioData[0].bioText}</h4> }
</>
}
</div>
</div>
)
}
export default Bio
Thanks in advance guys! Let me know if I can post routes or anything that might be helpful.

Resources