React x Sanity || Button to Fetching All posts of users, and Button to fetch only posts of who the users follow - javascript

I'm new to react and Im working on a capstone project. My project is a web-app that is of a social media-like website, and im having a hard time debugging my code for days now. Issue when I press the buttons "All recipes" or "Following FYP", console throws me the string of my groq query, but im pretty sure my queries work right. Here are my codes:
`import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { fetchUser } from '../utils/fetchUser';
import { client } from '../client';
import { feedQuery, searchQuery, userFollowingPost, userQuery } from '../utils/data';
import MasonryLayout from './MasonryLayout';
import Spinner from './Spinner';
const activeBtnStyles = 'bg-nGreen text-white font-bold p-2 rounded-full w-20 outline-none';
const notActiveBtnStyles = 'bg-primary mr-4 text-black font-bold p-2 rounded-full w-20 outline-none';
const Feed = () => {
const [pins, setPins] = useState();
const [loading, setLoading] = useState(false);
const { categoryId } = useParams();
const [user, setUser] = useState();
const [text, setText] = useState('All Recipes');
const [activeBtn, setActiveBtn] = useState('All Recipes');
const navigate = useNavigate();
const { userId } = useParams();
// call the fetchUser function to get the user data
const users = fetchUser();
useEffect(() => {
if (categoryId) {
setLoading(true);
const query = searchQuery(categoryId);
client.fetch(query).then((data) => {
setPins(data);
setLoading(false);
});}
else if (text === 'All Recipes'){
client.fetch(feedQuery)
.then((data) => {
setPins(data);
setLoading(false);
});}
else
{
// pass the user data as a variable to the userFollowingPost function
const followingpost = userFollowingPost(users);
client.fetch(followingpost)
.then((data) => {
setPins(data);});
}
}, [categoryId, text, userId]);
useEffect(() => {
const query = userQuery(userId);
client.fetch(query).then((data) => {
if(data && data[0]){setUser(data[0])}
});
}, [userId]);
const ideaName = categoryId || 'new';
if (loading) {
return (
<Spinner message={`We are adding ${ideaName} recipes to your feed!`} />
);
}
return (
<div>
<div className="text-center mb-7">
<button
type="button"
onClick={(e) => {
setText(e.target.textContent);
setActiveBtn('All Recipes');
}}
className={`${activeBtn === 'All Recipes' ? activeBtnStyles : notActiveBtnStyles}`}>
All Recipe
</button>
<button
type="button"
onClick={(e) => {
setText(e.target.textContent);
setActiveBtn('Following FYP');}}
className={`${activeBtn === 'Following FYP' ? activeBtnStyles : notActiveBtnStyles}`}>
Following FY </button>
</div>
{pins && (
<MasonryLayout pins={pins} />
)}
{pins?.length == 0 &&
<div className="mt-10 text-center text-xl ">No Recipes Found!</div>
}
</div >
);
};
export default Feed;
For reference, here are my queries saved in data.js
`export const feedQuery = `*[_type == "pin"] | order(_createdAt desc) {
image{
asset->{
url
}
},
_id,
procedure[],
ingredient[],
ingredientVal[],
metric[],
postedBy->{
_id,
userName,
image
},
save[]{
_key,
postedBy->{
_id,
userName,
image
},
},
} `;
export const userFollowingPost = (userId) => {
const query = `*[_type == "pin" && userId in *[_type == "user" && id == '${userId}'].following[].userId] | order(_createdAt desc) {
image{
asset->{
url
}
},
_id,
procedure[],
ingredient[],
ingredientVal[],
metric[],
postedBy->{
_id,
userName,
image
},
save[]{
_key,
postedBy->{
_id,
userName,
image
},
},
}`;
console.log(query)
return query;
};
What I want to achieve is when i press the button "All recipes" it would fetch all the posts(pins). And when I press the button "Following FYP", it would fetch all the posts(pins) of those posts of the users whom he/she follows

Related

How can I toggle one icon which triggers the disabling of another icon in React?

I am developing an app with react and redux toolkit. I am using an API. the part that I would like help with is this:
I have a favorite Icon. this icon takes a copy of the movie and displays it in the favorite section. There is another icon which is for watched movies. so the idea is that if I click on watched it should disable only the favorite icon of the movie card I clicked. however, it disables all favorite icons for all movie cards. The watched icon is referred to as Eye the favorite icon is referred to as star
This is the eye component (Watched)
import { useState } from "react";
import { BsEyeSlash, BsEyeFill } from "react-icons/bs";
import { useDispatch, useSelector } from "react-redux";
import {
add_watched,
remove_watched,
add_occupied,
remove_occupied,
} from "../features/animeSlice";
const Eye = ({ anime, type }) => {
const [active_eye, setActive_eye] = useState(false);
const dispatch = useDispatch();
const toggle_eye = () => {
if (!active_eye) {
setActive_eye((prev) => {
return !prev;
});
dispatch(add_watched(anime));
dispatch(add_occupied(type));
} else {
setActive_eye((prev) => {
return !prev;
});
dispatch(remove_watched(anime?.mal_id));
dispatch(remove_occupied());
}
};
return (
<div>
{!active_eye ? (
<BsEyeSlash
className="text-xl text-green-500 icons_1"
onClick={toggle_eye}
/>
) : (
<BsEyeFill
className={"text-xl text-red-500 icons_1"}
onClick={toggle_eye}
/>
)}
</div>
);
};
export default Eye;
This is the Star Component (Favorite)
import { useState } from "react";
import { AiOutlineStar, AiFillStar } from "react-icons/ai";
import { add_favourite, remove_favourite } from "../features/animeSlice";
import { useDispatch, useSelector } from "react-redux";
const Star = ({ anime }) => {
const { value} = useSelector((state) => state.anime);
const [active_star, setActive_star] = useState(false);
const dispatch = useDispatch();
const toggle_star = () => {
if (!active_star) {
setActive_star((prev) => {
return !prev;
});
dispatch(add_favourite(anime));
} else {
setActive_star((prev) => {
return !prev;
});
dispatch(remove_favourite(anime?.mal_id));
}
};
return (
<div>
{!active_star ? (
<AiOutlineStar
className={
value === "occupied"
? "text-xl text-gray-300 pointer-events-none"
: "text-xl text-yellow-500 icon_1"
}
onClick={toggle_star}
/>
) : (
<AiFillStar
className={
value === "occupied"
? "text-xl text-gray-300 pointer-events-none"
: "text-xl text-yellow-500 icon_1"
}
onClick={toggle_star}
/>
)}
</div>
);
};
export default Star;
this is the redux toolkit slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
favourite: [],
watched: [],
value: "",
page:""
};
const animeSlice = createSlice({
name: "anime",
initialState,
reducers: {
add_favourite(state, { payload }) {
state.favourite.push(payload);
},
remove_favourite(state, { payload }) {
state.favourite = state.favourite.filter(
(anime) => anime?.mal_id !== payload
);
},
add_watched(state, { payload }) {
state.watched.push(payload);
},
remove_watched(state, { payload }) {
state.watched = state.watched.filter((anime) => anime?.mal_id !== payload);
},
add_occupied(state, { payload }) {
state.value = payload;
},
remove_occupied(state) {
state.value = "";
},
pageNumber(state, { payload }) {
state.page = payload
}
},
});
export const {
add_favourite,
remove_favourite,
add_watched,
remove_watched,
add_occupied,
remove_occupied,
pageNumber
} = animeSlice.actions;
export default animeSlice.reducer;
This is the component that holds the component of eye and star
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import Eye from "./Eye";
import Star from "./Star";
const TopAnime = ({ anime }) => {
//pagination
//
//
let colorYear = (year) => {
if (year === "N/A") {
return "text-violet-500";
} else {
return "";
}
};
return (
<section className="grand px-4 pt-2 pb-5 w-64 bg-white/5 bg-opacity-80 backdrop-blur-sm rounded-lg cursor-pointer font-poppins animate-slideup">
<div className="pb-1 wrapper_icons">
<div className="wrapper_hover">
<Eye anime={anime} type="occupied" />
<Star anime={anime} />
</div>
</div>
<Link to={`/anime/${anime?.mal_id}`}>
<div className="wrapper_1 flex flex-col items-center justify-center">
<div className="h-[313px] w-[219px]">
<img
src={anime?.images?.jpg?.large_image_url}
alt={anime?.title}
className="h-full w-full"
/>
</div>
</div>
<div className="flex flex-col mt-3">
<p className="text-sm text-white truncate mx-1">
{anime?.title_english ? anime?.title_english : anime?.title}
</p>
<div className="flex justify-between items-center text-sm text-yellow-500 mx-1">
<p className={colorYear(anime?.year ? anime?.year : "N/A")}>
{anime?.year ? anime?.year : "N/A"}
</p>
<p
className={
anime?.score <= 7
? "text-cyan-500"
: anime?.score <= 5
? "text-red-600"
: "text-green-500"
}
>
{anime?.score}
</p>
</div>
</div>
</Link>
</section>
);
};
export default TopAnime;
This is where TopAnime is rendered
import { useGetAnimeQuery } from "../features/API";
import { useDispatch, useSelector } from "react-redux";
import { useState, useEffect } from "react";
import TopAnime from "../components/TopAnime";
import Spinner from "../components/Spinner";
import { NavLink, Link } from "react-router-dom";
import Paginator from "../components/Paginator";
//import ReactPaginate from "react-paginate";
import Pagination from "#mui/material/Pagination";
const Home = () => {
const [page, setPage] = useState(1);
const {
data: manga = [],
isLoading,
isFetching,
error,
} = useGetAnimeQuery(page);
const { data, pagination } = manga;
//destructuring objects
//const { data, pagination } = manga;
//const top_anime = data;
const total = Math.ceil(pagination?.items?.total / 24)
//const current_page = pagination?.current_page;
//const per_page = pagination?.items?.per_page;
//const { items: pages } = total;
/* let fetchData = async (page = 1) => {
let res = await fetch(
`https://api.jikan.moe/v4/top/anime?page=${page}&limit=24`
);
let query = await res.json();
const { data, pagination } = query;
let totalPages = Math.ceil(pagination?.items.total / 24);
setPageCount(totalPages);
setData(data);
};
useEffect(() => {
fetchData();
}, []);
*/
import { useGetAnimeQuery } from "../features/API";
import { useDispatch, useSelector } from "react-redux";
import { useState, useEffect } from "react";
import TopAnime from "../components/TopAnime";
import Spinner from "../components/Spinner";
import { NavLink, Link } from "react-router-dom";
import Paginator from "../components/Paginator";
//import ReactPaginate from "react-paginate";
import Pagination from "#mui/material/Pagination";
const Home = () => {
const [page, setPage] = useState(1);
const {
data: manga = [],
isLoading,
isFetching,
error,
} = useGetAnimeQuery(page);
const { data, pagination } = manga;
//destructuring objects
//const { data, pagination } = manga;
//const top_anime = data;
const total = Math.ceil(pagination?.items?.total / 24)
//const current_page = pagination?.current_page;
//const per_page = pagination?.items?.per_page;
//const { items: pages } = total;
/* let fetchData = async (page = 1) => {
let res = await fetch(
`https://api.jikan.moe/v4/top/anime?page=${page}&limit=24`
);
let query = await res.json();
const { data, pagination } = query;
let totalPages = Math.ceil(pagination?.items.total / 24);
setPageCount(totalPages);
setData(data);
};
useEffect(() => {
fetchData();
}, []);
*/
const handleChange = (event, value) => {
setPage(value);
};
const display = data?.map((anime) => (
<TopAnime anime={anime} key={anime.mal_id} />
));
//const pageCount = Math.ceil(pagination?.items?.total / 24);
if (isLoading) {
return <Spinner color_1={"#141e30"} color_2={"#243b55"} />;
} else if (isFetching) {
return <Spinner color_1={"#141e30"} color_2={"#243b55"} />;
} else if (error) {
console.log("ERROR =>", error.message);
}
return (
<section className="bg-gradient-to-r from-[#141e30] to-[#243b55]">
<div className="container font-poppins">
<div className="grid grid-cols-4 gap-3 place-items-center px-20">
{/* {top_anime &&
top_anime?.map((anime) => (
<TopAnime anime={anime} key={anime.mal_id} />
))} */}
{display}
</div>
<div className="button text-yellow-500 flex items-center justify-center mt-2 pb-2 cursor-pointer">
{/* <Paginator paginated={paginated} NumP={pagination?.last_visible_page} /> */}
{/* <ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
onPageChange={(page) => fetchData(page.selected + 1)}
pageCount={pageCount}
className="flex space-x-2"
activeClassName="active"
/> */}
<Pagination count={total} page={page} onChange={handleChange} defaultPage={1} boundaryCount={3} color="secondary" sx={{button:{color:'#ffffff'}}} />
</div>
</div>
</section>
);
};
export default Home;
Issue
The issue with the code/current implementation is that there is only one state.anime.value and one "occupied" value. Each time a movie's "watched" status is toggled the Redux state sets/unsets the single state.anime.value state. In other words, you can toggle N movies "watched" and the state.anime.value value is "occupied", but then toggling just 1 movie back to "unwatched" and state.anime.value is reset back to "". All the Star components read this single state value and this is why the stars all toggle together.
Solution
If you want a specific movie to toggle the Star component when the Eye icon is toggled then I think the solution is a bit simpler than you are making it out to be in your code.
The "eye" and "star" components effectively duplicate your Redux state and don't synchronize well with it. It's often considered a React anti-pattern to duplicate state. The state.anime.favourite and state.anime.watched arrays are all the app needs to know what has been marked "watched" and what has been favourited.
Update the animeSlice to store the anime.mal_id properties in objects instead of arrays to provide O(1) lookup in the UI. Remove the anime.value state as it's unnecessary.
animeSlice.js
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
favourite: {},
watched: {},
page: ""
};
const animeSlice = createSlice({
name: "anime",
initialState,
reducers: {
add_favourite(state, { payload }) {
state.favourite[payload] = true;
},
remove_favourite(state, { payload }) {
delete state.favourite[payload];
},
add_watched(state, { payload }) {
state.watched[payload] = true;
delete state.favourite[payload]; // un-star when adding as watched
},
remove_watched(state, { payload }) {
delete state.watched[payload];
},
pageNumber(state, { payload }) {
state.page = payload;
}
}
});
export const {
add_favourite,
remove_favourite,
add_watched,
remove_watched,
pageNumber
} = animeSlice.actions;
export default animeSlice.reducer;
Update the Eye and Star components to read the true state from the store. These components should pass anime.mal_id to the dispatched actions and use anime.mal_id to check the watched/favourites map objects.
Eye
import { BsEyeSlash, BsEyeFill } from "react-icons/bs";
import { useDispatch, useSelector } from "react-redux";
import { add_watched, remove_watched } from "../features/animeSlice";
const Eye = ({ anime }) => {
const dispatch = useDispatch();
const { watched } = useSelector((state) => state.anime);
const isWatched = watched[anime.mal_id];
const toggle_eye = () => {
dispatch((isWatched ? remove_watched : add_watched)(anime.mal_id));
};
const Eye = isWatched ? BsEyeFill : BsEyeSlash;
const className = [
"text-xl icons_1",
isWatched ? "text-green-500" : "text-red-500"
].join(" ");
return (
<div>
<Eye className={className} onClick={toggle_eye} />
</div>
);
};
export default Eye;
Star
import { AiOutlineStar, AiFillStar } from "react-icons/ai";
import { add_favourite, remove_favourite } from "../features/animeSlice";
import { useDispatch, useSelector } from "react-redux";
const Star = ({ anime }) => {
const { favourite, watched } = useSelector((state) => state.anime);
const dispatch = useDispatch();
const isFavorited = favourite[anime.mal_id];
const isWatched = watched[anime.mal_id];
const toggle_star = () => {
dispatch((isFavorited ? remove_favourite : add_favourite)(anime.mal_id));
};
const Star = isFavorited ? AiFillStar : AiOutlineStar;
const className = [
"text-xl",
isWatched ? "text-gray-300 pointer-events-none" : "text-yellow-500 icon_1"
].join(" ");
return (
<div>
<Star className={className} onClick={toggle_star} />
</div>
);
};
export default Star;

After click on like button the whole website become disappear but after refresh the webpage it increments why does it happens?

I am working with a video application website with a fake json server. Everything is going well but the problem appears while I want to like a video it becomes disappears and throwing an error: Objects are not valid as react child. Why this is happening?
The whole state slice:
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit"
import { getVideo, updateReaction } from "./videoApi"
//initializing the state
const initialState = {
video: {}, //while we are going to push a single video into the state we should put it an object instance...
loading: false,
isError: false,
error: ''
}
// async thunk for getting single video
export const fetchVideo = createAsyncThunk('vidoes/fetchVideo', async (id) => {
const videos = await getVideo(id);
return videos;
});
// another async thunk to react on a single video (like or dislike)
export const fetchReact = createAsyncThunk('video/reaction', async ({ id, reaction }) => {
const videoReaction = await updateReaction({ id, reaction })
return videoReaction;
})
//creating slice
const videoSlice = createSlice({
name: 'video',
initialState,
extraReducers: (builder) => {
builder.addCase(fetchVideo.pending, (state) => {
state.loading = true;
state.isError = false;
})
builder.addCase(fetchVideo.fulfilled, (state, action) => {
state.loading = false;
state.video= action.payload;
})
builder.addCase(fetchVideo.rejected, (state, action) => {
state.loading = false;
state.isError = true;
state.error = action.error?.message;
state.video = [];
})
//writing codes for emplementing like funcitonality
builder.addCase(fetchReact.pending, (state) => {
return state;
})
builder.addCase(fetchReact.fulfilled, (state, action) => {
state.video.likes = action.payload;
state.video.unlikes = action.payload;
})
builder.addCase(fetchReact.rejected, (state) => {
return state;
})
}
});
export default videoSlice.reducer;
This one is video api
import axios from '../../utils/axios'
export const getVideo = async (id) => {
const response = await axios.get(`/videos/${id}`)
return response.data;
}
export const updateReaction = async ({ id, reaction }) => {
console.log(id)
const { data } = await axios.get(`/videos/${id}`)
if (data) {
let updatedReaction =
reaction === 'like'
? {
likes: data.likes + 1,
}
: {
unlikes: data.unlikes + 1,
};
const response = await axios.patch(`/videos/${id}`, updatedReaction);
return response.data;
}
}
Video description component
import LIke from '../Like/LIke';
const VidoeDescription = ({ video }) => {
const { title, date, description, likes, unlikes, id } = video;
return (
<div>
<h1 className="text-lg font-semibold tracking-tight text-slate-800">
{title}
</h1>
<div className="pb-4 flex items-center space-between border-b">
<h2 className="text-sm leading-[1.7142857] text-slate-600 w-full">
Uploaded on {date}
</h2>
<LIke likes={likes} unlikes={unlikes} id={id} />
</div>
<div className="mt-4 text-sm text-[#334155] dark:text-slate-400">
{description}
</div>
</div>
);
};
export default VidoeDescription;
Like component where I put my like and unlike component
import React from 'react';
import { useDispatch } from 'react-redux';
import LikeImg from '../../assets/like.svg';
import UnLikeImg from '../../assets/unlike.svg'
// import { updateReaction } from '../../features/video/videoApi';
import { fetchReact } from '../../features/video/video_slice';
const LIke = ({ likes, unlikes, id }) => {
console.log(id)
const dispatch = useDispatch();
//handle like features
const reactionHandler = ({ id, reaction }) => {
dispatch(fetchReact({ id, reaction }))
}
return (
<div className="flex gap-10 w-48">
<div className="flex gap-1">
<div
className="shrink-0 cursor-pointer"
onClick={() => reactionHandler({ id, reaction: 'like' })}
>
<img className="w-5 block" src={LikeImg} alt="Like" />
</div>
<div className="text-sm leading-[1.7142857] text-slate-600">
{likes >= 1000 ? `${ likes }K` : likes}
</div>
</div>
<div className="flex gap-1">
<div
onClick={() => reactionHandler({ id, reaction: 'unlike' })}
className="shrink-0 cursor-pointer"
>
<img className="w-5 block" src={UnLikeImg} alt="Unlike" />
</div>
<div className="text-sm leading-[1.7142857] text-slate-600">
{unlikes >= 1000 ? `${ unlikes }K` : unlikes}
</div>
</div>
</div>
);
};
export default LIke;
This one is root video page
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import Player from '../component/Player/Player';
import VidoeDescription from '../component/VideoDescription/VidoeDescription';
import { fetchVideo } from '../features/video/video_slice';
import Loading from '../component/Loading/Loading';
import RelatedVideo from '../component/VideoList/RelatedVideo';
const Videos = () => {
const { loading, isError, video, error } = useSelector(state => state.video)
const { id, link, title , tags } = video || {};
const dispatch = useDispatch()
const { videoId } = useParams();
useEffect(() => {
dispatch(fetchVideo(videoId))
}, [dispatch, videoId])
//decide what to render in the ui
let content = null;
if (loading) {
content = <Loading />
}
if (loading && isError) {
content = <div className="col-span-12"><srong>{error}</srong></div>
}
if (!loading && !isError && video?.id) {
content = <div className="col-span-12"><srong>{error}</srong></div>
}
if (!loading && !isError && video?.id) {
content = <div className="mx-auto max-w-7xl px-2 pb-20 min-h-[400px]">
<div className="grid grid-cols-3 gap-2 lg:gap-8">
<div className="col-span-full w-full space-y-8 lg:col-span-2">
<Player link={link} title={title} />
<VidoeDescription video={video} />
</div>
<RelatedVideo currentId={id} tags={tags} />
</div>
</div>
}
return (
<div>
<section className="pt-6 pb-20">
{content}
</section>
</div>
);
};
export default Videos;
Problem
Here:
builder.addCase(fetchReact.fulfilled, (state, action) => {
state.video.likes = action.payload;
state.video.unlikes = action.payload;
})
action.payload looks like this:
{
likes: 1,
}
because it's how you created it in updateReaction
Solution
You wanted to do this instead:
builder.addCase(fetchReact.fulfilled, (state, action) => {
state.video.likes = action.payload.likes;
state.video.unlikes = action.payload.unlikes;
})

Firebase RTDB .update function not working?

I'm trying to update a node in a post reference in my Firebase Realtime Database, but for some reason it's not working. The console logs the full path, not too sure what I'm doing wrong. I've tried different variations of function creation, but none seem to be working. Any help here is greatly appreciated, thank you! Still relatively new to react.
Code:
import React, { useEffect, useContext } from "react";
import ReactDOM from "react-dom";
import { CSSTransition } from "react-transition-group";
import { useParams, useNavigate } from 'react-router-dom';
import { DatabaseContext } from '../contexts/database-context';
import Gallery from 'react-grid-gallery';
import '../scss/pages/HomeDetailPage.scss'
import { getAuth } from "firebase/auth";
import { getDatabase, onValue, ref, set, push, Database, DataSnapshot } from "firebase/database";
import "../scss/components/Modal.scss";
const Modal = props => {
//https://codesandbox.io/s/magical-christian-qxtdm?from-embed=&file=/src/App.js:111-151
const auth = getAuth();
const cUser = auth.currentUser;
const navigate = useNavigate();
const navigateTo = (path) => {
navigate(path)
}
const { GetSelectedListingDetail, GetListingOwner, selectedListing, listingOwner } = useContext(DatabaseContext);
const {
address,
bathrooms,
bedrooms,
creationDate,
description,
images,
postKey,
latitude,
longitude,
postRef,
postUrl,
poster,
price,
rawPrice,
squareFeet,
view_Count,
isHighlighted
} = selectedListing || '';
const { email } = listingOwner || '';
const params = useParams();
const closeOnEscapeKeyDown = e => {
if ((e.charCode || e.keyCode) === 27) {
props.onClose();
}
};
useEffect(() => {
document.body.addEventListener("keydown", closeOnEscapeKeyDown);
return function cleanup() {
document.body.removeEventListener("keydown", closeOnEscapeKeyDown);
};
}, []);
function writeUserData() {
const db = getDatabase();
const key = props.postKey;
const listingRef = db.ref('listings/' + key);
console.log(listingRef);
listingRef.update({
price: 'SOLD'
});
}
return ReactDOM.createPortal(
<CSSTransition
in={props.show}
unmountOnExit
timeout={{ enter: 0, exit: 300 }}
>
<div className="modal" onClick={props.onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<div className="home-detail-page-col-1">
<Gallery
images={Object.values(props.images).map((url) => ({
src: url,
thumbnail: url,
alt: 'alt txt'
}))}
enableImageSelection={false}
/>
</div>
<div className="home-detail-page-col-2">
<div className="detailDiv">
<div className='actionDiv'>
<button className='closeBtn' onClick={props.onClose}>+</button>
</div>
<div className="propInfo">
<p className="title is-2 mb-5">{props.price}</p>
<p className="subtitle is-6 mb-2">{props.address}</p>
<p className="subtitle is-6">{props.bedrooms + ' | ' + props.bathrooms + ' | ' + props.squareFeet}</p>
</div>
{
(cUser && cUser.uid !== props.poster) &&
<div className="contactBtn">
<button className="contactButton" href={`mailto:${props.email}`}>Contact Owner</button>
</div>
}
{
(cUser && cUser.uid === props.poster) &&
<div className="contactBtn">
<button className="contactButton" onClick={() => writeUserData()}>Mark as Sold</button>
</div>
}
{
!cUser &&
<div className="contactBtn">
<button className="contactButton" onClick={() => navigateTo('/signin')}>Sign in to contact</button>
</div>
}
<div className="propDesc">
<p className="title is-5">Property Overview:</p>
<p className="title is-6 pb-4">
{props.description}
</p>
</div>
</div>
</div>
</div>
</div>
</CSSTransition>,
document.getElementById("root")
);
};
export default Modal;
You are using the Firebase Modular SDK that has a functional syntax and update() is a function that can be imported from the Realtime Database SDK as shown below:
import { ref, update, getDatabase } from "firebase/database"
function writeUserData() {
const db = getDatabase();
const key = props.postKey;
const listingRef = ref(db, 'listings/' + key); // and not db.ref(...)
update(listingRef, {
price: 'SOLD'
}).then(() => {
console.log("Data updated")
}).catch((e) => console.log(e))
}

useState defaults appear to rerun after running function on state change, defaults shouldnt run twice

I have the following issue with website where the settings state resets after running more searches. The settings component is show below in the picture, it usually works but if you uncheck a box and then run a few more searches at some point the showAllDividends setting will be set to false, the All dividends component won't be on the screen, but for some reason the checkbox itself is checked (true). This is my first time really working with checkboxes in React, and I think I'm using the onChange feature wrong. Right now I get the event.target.checked boolean, but only onChange.
If that isn't the issue then the most likely cause is the default statements being run again on another render:
const [showMainInfo, setShowMainInfo] = useState(true);
const [showYieldChange, setShowYieldChange] = useState(true);
const [showAllDividends, setShowAllDividends] = useState(true);
the thing is I don't see why the default statements would run more than once, the component isn't being destroyed there's no react router usage. I expected it to keep its current state after the page is first loaded. I think the settings defaults are being rerun, but just don't understand why they would.
I unchecked, checked, unchecked the 'All dividends' checkbox, and it was unchecked when I ran 2 more searches. After the second search the checkbox was checked but the component was gone, because showAllDividends was false
main component SearchPage.js:
import React, {useState, useEffect} from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import SearchBar from './SearchBar';
import AllDividendsDisplay from './dividend_results_display/AllDividendsDisplay';
import DividendResultsDisplay from './dividend_results_display/DividendResultsDisplay';
import SettingsView from './settings/SettingsView';
const HOST = process.env.REACT_APP_HOSTNAME
const PROTOCOL = process.env.REACT_APP_PROTOCOL
const PORT = process.env.REACT_APP_PORT
const BASE_URL = PROTOCOL + '://' + HOST + ':' + PORT
const SearchPage = ({userId}) => {
const DEFAULT_STOCK = 'ibm';
const [term, setTerm] = useState(DEFAULT_STOCK);
const [debouncedTerm, setDebouncedTerm] = useState(DEFAULT_STOCK);
const [loading, setLoading] = useState(false);
const [recentSearches, setRecentSearches] = useState([DEFAULT_STOCK]);
const [dividendsYearsBack, setDividendsYearsBack] = useState('3');
const [debouncedDividendYearsBack, setDebouncedDividendYearsBack] = useState('3');
const [errorMessage, setErrorMessage] = useState('');
const [dividendsData, setDividendsData] = useState(
{
current_price: '',
recent_dividend_rate: '',
current_yield: '',
dividend_change_1_year: '',
dividend_change_3_year: '',
dividend_change_5_year: '',
dividend_change_10_year: '',
all_dividends: [],
name: '',
description: '',
}
)
const [settingsViewVisible, setSettingsViewVisible] = useState(false);
const [showMainInfo, setShowMainInfo] = useState(true);
const [showYieldChange, setShowYieldChange] = useState(true);
const [showAllDividends, setShowAllDividends] = useState(true);
const onTermUpdate = (term) => {
setTerm(term)
}
// TODO: write a custom hook that debounces taking the term and the set debounced term callback
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedTerm(term);
}, 1500);
return () => {
clearTimeout(timerId);
};
}, [term]);
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedDividendYearsBack(dividendsYearsBack);
}, 1500);
return () => {
clearTimeout(timerId);
};
}, [dividendsYearsBack]);
useEffect(() => {runSearch()}, [debouncedTerm]);
useEffect(() => {
// alert(dividendsYearsBack)
if (dividendsYearsBack !== '') {
runSearch();
}
}, [debouncedDividendYearsBack])
useEffect(() => {
console.log("user id changed")
if (userId) {
const user_profile_api_url = BASE_URL + '/users/' + userId
axios.get(user_profile_api_url, {})
.then(response => {
const recent_searches_response = response.data.searches;
const new_recent_searches = [];
recent_searches_response.map(dict => {
new_recent_searches.push(dict.search_term)
})
setRecentSearches(new_recent_searches);
})
.catch((error) => {
console.log("error in getting user profile: ", error.message)
})
}
}, [userId])
useEffect(() => {
const user_profile_api_url = BASE_URL + '/users/' + userId
const request_data = {searches: recentSearches}
axios.post(user_profile_api_url, request_data)
// .then(response => {
// console.log(response)
// })
}, [recentSearches])
const makeSearchApiRequest = () => {
let dividends_api_url = BASE_URL + '/dividends/' + term + '/' + dividendsYearsBack
if (!recentSearches.includes(term)) {
setRecentSearches([...recentSearches, term])
}
axios.get(dividends_api_url, {})
.then(response => {
// console.log(response)
setLoading(false);
setDividendsData(response.data);
})
.catch((error) => {
console.log(error.message);
setLoading(false);
setErrorMessage(error.message);
})
}
const runSearch = () => {
console.log("running search: ", term);
setErrorMessage('');
if (term) {
setLoading(true);
if (!dividendsYearsBack) {
setDividendsYearsBack('3', () => {
makeSearchApiRequest()
});
} else {
makeSearchApiRequest()
}
}
}
const recentSearchOnClick = (term) => {
setTerm(term);
setDebouncedTerm(term);
}
const removeRecentSearchOnClick = (term) => {
const searchesWithoutThisOne = recentSearches.filter(search => search !== term)
setRecentSearches(searchesWithoutThisOne);
}
const dividendsYearsBackOnChange = (text) => {
setDividendsYearsBack(text);
}
const renderMainContent = () => {
if (!debouncedTerm) {
return (
<div className="ui active">
<div className="ui text">Search for info about a stock</div>
</div>
)
}
if (loading === true) {
return (
<div className="ui active dimmer">
<div className="ui big text loader">Loading</div>
</div>
)
}
if (errorMessage) {
return (
<div className="ui active">
<div className="ui text">{errorMessage}</div>
</div>
)
} else {
return (
<DividendResultsDisplay
data={dividendsData}
dividends_years_back={dividendsYearsBack}
dividendsYearsBackOnChange={dividendsYearsBackOnChange}
showMainInfo={showMainInfo}
showYieldChange={showYieldChange}
showAllDividends={showAllDividends}/>
)
}
}
// https://stackoverflow.com/questions/38619981/how-can-i-prevent-event-bubbling-in-nested-react-components-on-click
const renderRecentSearches = () => {
return recentSearches.map((term) => {
return (
<div key={term}>
<button
onClick={() => recentSearchOnClick(term)}
style={{marginRight: '10px'}}
>
<div>{term} </div>
</button>
<button
onClick={(event) => {event.stopPropagation(); removeRecentSearchOnClick(term)}}>
X
</button>
<br/><br/>
</div>
)
})
}
const renderSettingsView = (data) => {
if (settingsViewVisible) {
return (
<SettingsView data={data} />
)
} else {
return null;
}
}
const toggleSettingsView = () => {
setSettingsViewVisible(!settingsViewVisible);
}
const toggleDisplay = (e, setter) => {
setter(e.target.checked)
}
const SETTINGS_DATA = [
{
label: 'Main info',
id: 'main_info',
toggler: toggleDisplay,
setter: setShowMainInfo
},
{
label: 'Yield change',
id: 'yield_change',
toggler: toggleDisplay,
setter: setShowYieldChange
},
{
label: 'Dividends list',
id: 'all_dividends',
toggler: toggleDisplay,
setter: setShowAllDividends
},
]
console.log("showMainInfo: ", showMainInfo);
console.log("showYieldChange: ", showYieldChange);
console.log("showAllDividends: ", showAllDividends);
return (
<div className="ui container" style={{marginTop: '10px'}}>
<SearchBar term={term} onTermUpdate={onTermUpdate} />
{renderRecentSearches()}
<br/><br/>
<button onClick={toggleSettingsView}>Display settings</button>
{renderSettingsView(SETTINGS_DATA)}
<div className="ui segment">
{renderMainContent()}
</div>
</div>
)
}
const mapStateToProps = state => {
return { userId: state.auth.userId };
};
export default connect(
mapStateToProps
)(SearchPage);
// export default SearchPage;
the settingsView component:
import React from 'react';
import SettingsCheckbox from './SettingsCheckbox';
const SettingsView = ({data}) => {
const checkboxes = data.map((checkbox_info) => {
return (
<div key={checkbox_info.id}>
<SettingsCheckbox
id={checkbox_info.id}
label={checkbox_info.label}
toggler={checkbox_info.toggler}
setter={checkbox_info.setter}/>
<br/>
</div>
)
});
return (
<div className="ui segment">
{checkboxes}
</div>
);
}
export default SettingsView;
SettingsCheckbox.js:
import React, {useState} from 'react';
const SettingsCheckbox = ({id, label, toggler, setter}) => {
const [checked, setChecked] = useState(true)
return (
<div style={{width: '200px'}}>
<input
type="checkbox"
checked={checked}
id={id}
name={id}
value={id}
onChange={(e) => {
setChecked(!checked);
toggler(e, setter);
}} />
<label htmlFor="main_info">{label}</label><br/>
</div>
);
}
export default SettingsCheckbox;

How to grab (orderId) payload value in form body to update the status of the order?

I am working on mern stack e-commerce project . I have a order update route . Order status get updated only by the admin . While updating order status admin gets the default(preload) status . Then admin enter new status for updation .
When I enter (accept) into input fields and hit update button it shows this and status may not get updated .
I am not able to grab orderID .
Here is my update status backend controller
exports.updateStatus = (req, res) => { Order.updateOne(
{ _id: req.body.orderId },
{ $set: { status: req.body.status } },
{ new: true, useFindAndModify: false },
(err, order) => {
if (err) {
return res.status(400).json({ error: "Cannot update order status" });
}
res.json(order);
} );};
update order route
router.put(
"/order-update/:userId",
isSignedIn,
isAuthenticated,
isAdmin,
updateStatus
);
API handler for frontend
export const updateOrder = (userId, token, order) => {
return fetch(`${API}/order-update/${userId}`, {
method: "PUT",
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(order),
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
Form for updating status
import React, { useEffect, useState } from "react";
import { isAutheticated } from "../auth/helper";
import Base from "../core/Base";
import { getOrder, updateOrder } from "./helper/adminapicall";
const UpdateOrder = ({ match }) => {
const { user, token } = isAutheticated();
const [status, setStatus] = useState("");
const [error, setError] = useState(false);
const [success, setSuccess] = useState(false);
const preload = (orderId) => {
getOrder(orderId, user._id, token).then((data) => {
console.log(data);
if (data?.error) {
setError(data?.error);
} else {
setStatus(data.status);
setError("");
setSuccess("");
}
});
};
useEffect(() => {
preload(match.params.orderId);
}, []);
const handleChange = (event) => {
setError(false);
setStatus(event.target.value);
setSuccess(false);
};
const onSubmit = (e) => {
e.preventDefault();
setError("");
setSuccess("");
updateOrder(user._id, token, { status }).then((data) => {
if (data?.error) {
setError(true);
} else {
setError("");
setSuccess(true);
setStatus("");
}
});
};
const successMessage = () => (
<div
className="alert alert-success mt-3"
style={{ display: success ? "" : "none" }}
>
<h4>updation successfull</h4>
</div>
);
const warningMessage = () => (
<div
className="alert alert-success mt-3"
style={{ display: error ? "" : "none" }}
>
<h4>updation failedl</h4>
</div>
);
const orderUpdateForm = () => (
<form>
<div className="form-group col-md-6">
<p className="lead">Update Order Status</p>
<input
type="text"
className="form-control my-3 col-md-6"
onChange={handleChange}
value={status}
autoFocus
required
/>
<button onClick={onSubmit} className="btn btn-outline-info col-md-6">
Update Order
</button>
</div>
</form>
);
return (
//
<Base title="Update Order Status" description="">
{successMessage()}
{warningMessage()}
{orderUpdateForm()}
</Base>
//
);
};
export default UpdateOrder;
You are trying to fetch orderId from the body but you are not sending OrderId in the body. Also, I am not able to understand why you sen UserId in route "/order-update/:userId".
I think you can send orderId as request param from the front end also you can update your route as
router.put(
"/order-update/:orderId",
isSignedIn,
isAuthenticated,
isAdmin,
updateStatus
);
After that, you can get orderId as
let { orderId } = req.params;

Categories

Resources