Use of custom API hooks with updating search terms - javascript

I'd like to use custom api request hook like so
// hooks.js
import { useState, useEffect } from 'react';
import api from '../utils/api';
function useAPI(fn, payload) {
const [state, setState] = useState({
loading: false,
data: null,
error: null,
});
const callAPI = async () => {
const setAPIState = update => setState({ ...state, ...update });
try {
setAPIState({ loading: true });
const data = await fn(payload);
setAPIState({ data, loading: false });
} catch (error) {
setAPIState({ error, loading: false });
}
};
useEffect(() => {
callAPI();
}, [fn, payload]);
return state;
}
export const useFetchTransactions = payload => {
const fetchTransactions = api.fetchTransactions;
return useAPI(fetchTransactions, payload);
};
and call it within my React component like so:
// Component.jsx
import { useState } from 'react';
import { useFetchTransactions } from '../hooks.js';
export default const Component = () => {
const [id, setID] = useState('');
const [date, setDate] = useState('12/13/21');
const { loading, data, error } = useFetchTransactions({ id, date });
return (
<div>{data.map...}</div>
)
}
However, this looks wrong. I think useFetchTransactions should live inside of a useEffect, so that the component then becomes something like:
// Updated Component.jsx
import { useState, useEffect } from 'react';
import { useFetchTransactions } from '../hooks.js';
export default const Component = () => {
const [id, setID] = useState('');
const [date, setDate] = useState('12/13/21');
const [apiState, setApiState] = useState({
data: null,
error: null,
loading: false
})
useEffect(() => {
const result = useFetchTransactions({ id, date });
setState({ ...apiState, ...result });
}, [date, id])
return (
<div>{apiState.data.map...}</div>
)
}
But the above feels cumbersome and redundant. Can anyone please lend some knowledge on this? Thanks!

You can do it like this
function useAPI(fn) {
const [state, setState] = useState({
loading: false,
data: null,
error: null,
});
const callAPI = async (payload) => {
const setAPIState = update => setState({ ...state, ...update });
try {
setAPIState({ loading: true });
const data = await fn(payload);
setAPIState({ data, loading: false });
} catch (error) {
setAPIState({ error, loading: false });
}
};
return {state, callAPI};
}
export const useFetchTransactions = () => {
const fetchTransactions = api.fetchTransactions;
return useAPI(fetchTransactions);
};
And then in your component
import { useState } from 'react';
import { useFetchTransactions } from '../hooks.js';
export default const Component = () => {
const [id, setID] = useState('');
const [date, setDate] = useState('12/13/21');
const { callAPI, loading, data, error } = useFetchTransactions();
useEffect(() => {
callAPI({ id, date })
}, [date, id])
return (
<div>{data.map...}</div>
)
}

Related

How can I make the data inside of my Cart become persistent?

I have a NextJS application that is using the ShopifyBuy SDK. I have been successfully able to implement a solution where I am able to fetch the products from Store and display them to the User. The user is also able to go to a product page and add the product to the cart.
However, when the user refreshes the page, the cart is reset, and the data does not persist. The code is below:
context/cart.js:
import { createContext, useContext, useEffect, useReducer } from "react";
import client from "../lib/client";
import Cookies from "js-cookie";
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = "SET_CART";
const initalState = {
lineItems: [],
totalPrice: 0,
webUrl: "",
id: "",
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const savedState = Cookies.get("cartState");
const [state, dispatch] = useReducer(reducer, savedState || initalState);
useEffect(() => {
Cookies.set("cartState", state, { expires: 7 });
}, [state]);
useEffect(() => {
getCart();
}, []);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
const getCart = async () => {
try {
const cart = await client.checkout.create();
setCart(cart);
} catch (err) {
console.log(err);
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={{ state }}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);
products/[handle].tsx:
import React, { useState, useEffect } from "react";
import client from "../../lib/client";
import { useCartDispatch, useCartState } from "../../context/cart";
import Link from "next/link";
import cookie from "js-cookie";
export const getStaticPaths = async () => {
const res = await client.product.fetchAll();
const paths = res.map((product: any) => {
return {
params: { handle: product.handle.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context: any) => {
const handle = context.params.handle;
const res = await client.product.fetchByHandle(handle);
const product = JSON.stringify(res);
return {
props: {
product,
},
};
};
function Product({ product }: any) {
const { state } = useCartState();
const { setCart } = useCartDispatch();
const addToCart = async () => {
const checkoutId = state.id;
const lineItemsToAdd = [
{
variantId: product.variants[0].id,
quantity: 1,
},
];
const res = await client.checkout.addLineItems(checkoutId, lineItemsToAdd);
setCart(res);
};
product = JSON.parse(product);
return (
<div>
<div className=" flex-col text-2xl font-bold m-8 flex items-center justify-center ">
<h1>{product.title}</h1>
<button onClick={addToCart}>Add to Cart</button>
<Link href="/cart">Checkout</Link>
</div>
</div>
);
}
export default Product;
pages/cart/index.tsx:
import React, { useEffect } from "react";
import { useCartState, useCartDispatch } from "../../context/cart";
import client from "../../lib/client";
function Cart() {
const { state } = useCartState();
return (
<div>
<h1>Cart</h1>
{state.lineItems &&
state.lineItems.map((item: any) => {
return (
<div key={item.id}>
<h2>{item.title}</h2>
<p>{item.variant.title}</p>
<p>{item.quantity}</p>
</div>
);
})}
</div>
);
}
export default Cart;
I have tried using a library called js-cookie and also localStorage. I'm not sure where the problem lies or if the solutions that I've tried are wrong.
P.S.: I'm fairly new to NextJS and Typescript so go easy on the syntax. This code is for a personal project. Thanks in advance!
Answering this because I ended up coming up with a solution that works for me, at least.
Here it is:
const getCart = async () => {
try {
const checkoutId = Cookies.get("checkoutId");
let cart;
if (checkoutId) {
cart = await client.checkout.fetch(checkoutId);
} else {
cart = await client.checkout.create();
Cookies.set("checkoutId", cart.id);
}
setCart(cart);
} catch (err) {
console.log(err);
}
};
From my understanding, what this does is the following:
Check the cookies to see if one exists called "checkoutId"
If it exists, fetch the cart using that checkoutId
Otherwise, create a new cart and create a cookie using the cart.id that is returned in the response
Then, inside my individual Product page ([handle].tsx), I'm doing the following:
const addToCart = async () => {
const checkoutId = state.id;
const lineItemsToAdd = [
{
variantId: product.variants[0].id,
quantity: 1,
},
];
const res = await client.checkout.addLineItems(checkoutId, lineItemsToAdd);
console.log(res);
if (cookie.get("checkoutId") === undefined) {
cookie.set("checkoutId", res.id);
}
setCart(res);
};
Using cookies to store your object cart, as far as I know, is not a good idea. You could use localStorage, like so:
import { createContext, useContext, useEffect, useReducer } from "react";
import client from "../lib/client";
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = "SET_CART";
const initalState =
typeof localStorage !== "undefined" && localStorage.getItem("cartState")
? JSON.parse(localStorage.getItem("cartState"))
: {
lineItems: [],
totalPrice: 0,
webUrl: "",
id: "",
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initalState);
useEffect(() => {
localStorage.set("cartState", JSON.stringify(state));
}, [state]);
useEffect(() => {
getCart();
}, []);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
const getCart = async () => {
try {
const cart = await client.checkout.create();
setCart(cart);
} catch (err) {
console.log(err);
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={{ state }}>{children}</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);

I am getting null as result in useSelector from react redux toolkit

and also useEffect also not working in my file. i tried many way but useEffect not working. if i comments everything file and try then useEffect working else not working.
and also i am getting null value from
const { currentVideo } = useSelector((state) => state.video);
this is my useEffect ->
useEffect(() => {
const fetchData = async () => {
try {
const videoRes = await axiosInstance.get(`/videos/find/${path}`, {
withCredentials: true,
});
// console.log(videoRes);
const updatedView = await axiosInstance.put(`videos/view/${path}`);
// console.log(updatedView.data, "view is updating");
const channelRes = await axiosInstance.get(
`/users/find/${videoRes.data.userId}`,
{ withCredentials: true }
);
setChannel(channelRes.data);
dispatch(fetchSuccess(videoRes.data));
} catch (err) {
console.log(err);
return "opps something went wrong!";
}
};
fetchData();
}, [path, dispatch]);
here i am getting null value
const Video = () => {
const { currentUser } = useSelector((state) => state.user);
const { currentVideo } = useSelector((state) => state.video);
const dispatch = useDispatch();
const path = useLocation().pathname.split("/")[2];
// console.log(currentVideo); // getting null
const [channel, setChannel] = useState({});
and my full video.jsx is
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import ThumbUpOutlinedIcon from "#mui/icons-material/ThumbUpOutlined";
import ThumbDownOffAltOutlinedIcon from "#mui/icons-material/ThumbDownOffAltOutlined";
import ReplyOutlinedIcon from "#mui/icons-material/ReplyOutlined";
import AddTaskOutlinedIcon from "#mui/icons-material/AddTaskOutlined";
import ThumbUpIcon from "#mui/icons-material/ThumbUp";
import ThumbDownIcon from "#mui/icons-material/ThumbDown";
import Comments from "../components/Comments";
import { useDispatch, useSelector } from "react-redux";
import { useLocation } from "react-router-dom";
import axios from "axios";
import { fetchSuccess, like, dislike } from "../redux/videoSlice";
import axiosInstance from "../axios";
import { subscription } from "../redux/userSlice";
import Recommendation from "../components/Recommendation";
import { format } from "timeago.js";
const Video = () => {
const { currentUser } = useSelector((state) => state.user);
const { currentVideo } = useSelector((state) => state.video);
const dispatch = useDispatch();
const path = useLocation().pathname.split("/")[2];
// console.log(currentVideo); // getting null
const [channel, setChannel] = useState({});
/*
// console.log(path); ok hai video user id aa rahi hai.
// its working its getting all the data.
const test = async () => {
const isWorking = await axios.get(
"http://localhost:5000/api/videos/find/63931e44de7c22e61c4ffd6c"
);
console.log(isWorking.data);
console.log(isWorking.data.videoUrl);
};
const videoRes = test();
console.log(videoRes);
*/
// {withCredentials: true}
useEffect(() => {
const fetchData = async () => {
try {
const videoRes = await axiosInstance.get(`/videos/find/${path}`, {
withCredentials: true,
});
// console.log(videoRes);
const updatedView = await axiosInstance.put(`videos/view/${path}`);
// console.log(updatedView.data, "view is updating");
const channelRes = await axiosInstance.get(
`/users/find/${videoRes.data.userId}`,
{ withCredentials: true }
);
setChannel(channelRes.data);
dispatch(fetchSuccess(videoRes.data));
} catch (err) {
console.log(err);
return "opps something went wrong!";
}
};
fetchData();
}, [path, dispatch]);
const handleLike = async () => {
try {
await axiosInstance.put(`/users/like/${currentVideo._id}`, {
withCredentials: true,
});
dispatch(like(currentUser._id));
} catch (err) {
console.log(err);
return "opps something went wrong!";
}
};
const handleDislike = async () => {
try {
await axiosInstance.put(`/users/dislike/${currentVideo._id}`, {
withCredentials: true,
});
dispatch(dislike(currentUser._id));
} catch (err) {
console.log(err);
}
};
const handleSub = async () => {
currentUser.subscribedUsers.includes(channel._id)
? await axiosInstance.put(`/users/unsub/${channel._id}`, {
withCredentials: true,
})
: await axiosInstance.put(`/users/sub/${channel._id}`, {
withCredentials: true,
});
dispatch(subscription(channel._id));
};
if (!currentUser) return "Loading....";
return (
<Container>
<Content>
<VideoWrapper>
<VideoFrame src={currentVideo?.videoUrl} controls />
</VideoWrapper>
<Title>{currentVideo?.title}</Title>
<Details>
<Info>
{currentVideo?.views} views •{format(currentVideo?.createdAt)}
</Info>
<Buttons>
<Button onClick={() => handleLike()}>
{currentVideo?.likes.includes(currentUser._id) ? (
<ThumbUpIcon />
) : (
<ThumbUpOutlinedIcon />
)}{" "}
{currentVideo?.likes.length}
</Button>
<Button onClick={() => handleDislike()}>
{currentVideo?.dislikes.includes(currentUser._id) ? (
<ThumbDownIcon />
) : (
<ThumbDownOffAltOutlinedIcon />
)}
Dislike
</Button>
<Button>
<ReplyOutlinedIcon /> Share
</Button>
<Button>
<AddTaskOutlinedIcon /> Save
</Button>
</Buttons>
</Details>
<Hr />
<Channel>
<ChannelInfo>
<Image src={channel.img} />
<ChannelDetail>
<ChannelName>{channel.name}</ChannelName>
<ChannelCounter>{channel.subscribers} subscribers</ChannelCounter>
<Description>{currentVideo?.desc}</Description>
</ChannelDetail>
</ChannelInfo>
<Subscribe onClick={handleSub}>
{currentUser.subscribedUsers.includes(channel._id)
? "SUBSCRIBED"
: "SUBSCRIBE"}
</Subscribe>
</Channel>
<Hr />
<Comments videoId={currentVideo?._id} />
</Content>
<Recommendation tags={currentVideo?.tags} />
</Container>
);
};
export default Video;
and this is my redux videoslice file videoSlice.js
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
currentVideo: null,
loading: false,
error: false,
};
export const videoSlice = createSlice({
name: "video",
initialState,
reducers: {
fetchStart: (state) => {
state.loading = true;
},
fetchSuccess: (state, action) => {
state.loading = false;
state.currentVideo = action.payload;
},
fetchFailure: (state) => {
state.loading = false;
state.error = true;
},
like: (state, action) => {
if (!state.currentVideo.likes.includes(action.payload)) {
state.currentVideo.likes.push(action.payload);
state.currentVideo.dislikes.splice(
state.currentVideo.dislikes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
dislike: (state, action) => {
if (!state.currentVideo.dislikes.includes(action.payload)) {
state.currentVideo.dislikes.push(action.payload);
state.currentVideo.likes.splice(
state.currentVideo.likes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
views: (state, action) => {
if (state.currentVideo.views.includes(action.payload)) {
state.currentVideo.views.push(action.payload);
}
},
},
});
export const { fetchStart, fetchSuccess, fetchFailure, like, dislike, views } =
videoSlice.actions;
export default videoSlice.reducer;
in browser i am also getting null
i am trying to call my api and render data. but useeffect not working in my code. also from useSelector i am getting null value.

Database updates only on the first click of the button with this functions, what is wrong and how could i fix it?

I am new to react and MongoDB, I am trying to add months to a date in my database in mongo, but it only updates the first time I click on the <Price> button, I need it to update every time I click it. The user has to log out and log back in for it to work again, but still only 1 update can be made to the database. Can someone explain to me why this is happening, and how could it be fixed?
This is the function
import React, { useContext } from "react";
import { useState } from "react";
import useFetch from "../../hooks/useFetch";
import Footer from "../../components/Footer";
import Navbar from "../../components/Navbar";
import Sidebar from "../../components/Sidebar";
import {
ContractContainer,
HeadingContainer,
TypeH1,
ActiveUntil,
MonthlyWrapper,
MonthlyContainer,
MonthNumber,
Price,
Navbarback,
} from "./userinfoElements";
import { AuthContext } from "../../context/AuthContext";
import moment from "moment";
import axios from "axios";
const Userinfo = () => {
// for nav bars
const [isOpen, setIsOpen] = useState(false);
// set state to true if false
const toggle = () => {
setIsOpen(!isOpen);
};
const { user } = useContext(AuthContext);
let { data, loading, reFetch } = useFetch(`/contracts/${user.contractType}`);
let dateFormat = moment(user.activeUntil).format("DD/MMMM/yyyy");
const updateDate = async () => {
try {
let newDate = moment(user.activeUntil).add(1, "months");
dateFormat = newDate.format("DD/MMMM/yyyy");
axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
} catch (err) {
console.log(err);
}
reFetch();
};
return (
<>
<Sidebar isOpen={isOpen} toggle={toggle} />
{/* navbar for smaller screens*/}
<Navbar toggle={toggle} />
<Navbarback /> {/* filling for transparent bacground navbar*/}
{loading ? (
"Loading components, please wait"
) : (
<>
<ContractContainer>
<HeadingContainer>
<TypeH1>{data.contractType}</TypeH1>
<ActiveUntil>Subscription active until {dateFormat}</ActiveUntil>
</HeadingContainer>
<MonthlyWrapper>
<MonthlyContainer>
<MonthNumber>1 Month</MonthNumber>
<Price onClick={updateDate}>{data.month1Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>3 Month</MonthNumber>
<Price onClick={updateDate}>{data.month3Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>6Month</MonthNumber>
<Price onClick={updateDate}>{data.month6Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>12Month</MonthNumber>
<Price onClick={updateDate}>{data.month12Price}$</Price>
</MonthlyContainer>
</MonthlyWrapper>
</ContractContainer>
</>
)}
<Footer />
</>
);
};
export default Userinfo;
this is the fetch hook
import { useEffect, useState } from "react";
import axios from "axios";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
fetchData();
}, [url]);
const reFetch = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
return { data, loading, error, reFetch };
};
export default useFetch;
Any help is appreciated!
EDIT: added AuthContext file and server sided controllers if needed
import React from "react";
import { createContext, useEffect, useReducer } from "react";
const INITIAL_STATE = {
user: JSON.parse(localStorage.getItem("user")) || null,
loading: false,
error: null,
};
export const AuthContext = createContext(INITIAL_STATE);
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN_START":
return {
user: null,
loading: true,
error: null,
};
case "LOGIN_SUCCESS":
return {
user: action.payload,
loading: false,
error: null,
};
case "LOGIN_FAILURE":
return {
user: null,
loading: false,
error: action.payload,
};
case "LOGOUT":
return {
user: null,
loading: false,
error: null,
};
default:
return state;
}
};
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
useEffect(() => {
localStorage.setItem("user", JSON.stringify(state.user));
}, [state.user]);
return (
<AuthContext.Provider
value={{
user: state.user,
loading: state.loading,
error: state.error,
dispatch,
}}
>
{children}
</AuthContext.Provider>
);
};
api Controller to update active date
import User from "../models/User.js";
export const updateActiveDate = async (req, res, next) => {
try {
await User.updateOne({ $set: { activeUntil: req.body.activeUntil } });
res.status(200).json("Active date has been updated.");
} catch (err) {
next(err);
}
};
api Controller to find contracts
import Contracts from "../models/Contracts.js";
export const getContract = async (req, res, next) => {
try {
const Contract = await Contracts.findOne({
contractType: req.params.contractType,
});
res.status(200).json(Contract);
} catch (err) {
next(err);
}
};
api Controller for login authentication
export const login = async (req, res, next) => {
try {
const user = await User.findOne({ namekey: req.body.namekey });
if (!user) return next(createError(404, "User not found!"));
if (req.body.password === undefined) {
return next(createError(500, "Wrong password or namekey!"));
}
const isPasswordCorrect = await bcrypt.compare(
req.body.password,
user.password
);
if (!isPasswordCorrect)
return next(createError(400, "Wrong password or namekey!"));
const token = jwt.sign({ id: user._id }, process.env.JWT);
const { password, ...otherDetails } = user._doc;
res
.cookie("access_token", token, {
httpOnly: true,
})
.status(200)
.json({ details: { ...otherDetails } });
} catch (err) {
next(err);
}
};
You should update the stored user state to reflect the activeUntil date change.
Define a 'UPDATE_USER_DATE' action in your reducer to update the user instance:
case "UPDATE_USER_DATE":
const updatedUser = { ...state.user };
updatedUser.activeUntil = action.payload;
return {
...state,
user: updatedUser
};
Then, after updating the date in updateDate, update the user state as well:
const { user, dispatch } = useContext(AuthContext);
const updateDate = async () => {
try {
let newDate = moment(user.activeUntil).add(1, "months");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
} catch (err) {
console.log(err);
}
reFetch();
};
Give this a try. It awaits the put request, and only once that has responded it calls reFetch. Without the await you're calling the reFetch before the put request has had a chance to complete its work.
const updateDate = async () => {
try {
let newDate = moment(user.activeUntil).add(1, "months");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
} catch (err) {
console.log(err);
} finally {
reFetch();
}
};

What is Problem TypeError: action.value is undefined? (React)

This is the file that I retrieved data from Firebase's table. I have no problem here. Here I have logged the console to see all the data. My problem is in the quiz.js file
useQuestion.js
import { getDatabase, ref, query, orderByKey, get } from "firebase/database";
import { useEffect, useState } from "react";
export default function useQuestions(yID) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const [questions, setQuestions] = useState();
useEffect(() => {
// get data from database
async function fetchQuestion() {
const db = getDatabase();
const questionRef = ref(db, "quiz/" + yID + "/questions");
const questionQuery = query(questionRef, orderByKey());
try {
setError(false);
setLoading(true);
const snapshot = await get(questionQuery);
setLoading(false);
if (snapshot.exists()) {
setQuestions((prevQuestions) => {
return [...prevQuestions, ...Object.values(snapshot.val())];
});
}
} catch (e) {
setLoading(false);
console.log(e);
setError("You are Press Wrong Questions.");
}
}
fetchQuestion();
}, [yID]);
return {
loading,
error,
questions,
};
}
The problem with this file is useReducer. I set the dispatch value but showing action.value is undefined. why this problem in my project.
quiz.js
import { useEffect, useReducer, useState } from "react";
import useQuestions from "../../hooks/useQuestion";
import { useParams } from "react-router";
import Answare from "../../answare";
import MiniPlayer from "../../miniPlayer";
import PogressBar from "../../pogressBar";
import _ from "lodash";
const initialState = null;
const reducer = (state, action) => {
switch (action.type) {
case "questions":
action.value.forEach((question) => {
question.options.forEach((option) => {
option.checked = false;
});
});
return action.value;
case "answer":
const questions = _.cloneDeep(state);
questions[action.questionID].options[action.optionIndex].checked =
action.value;
return questions;
default:
return state;
}
};
function Quiz() {
const { id } = useParams();
const [currentQuestions, setCurrentQuestions] = useState(0);
const { loading, error, questions } = useQuestions(id);
const [qna, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
dispatch({
type: "questions",
value: questions,
});
}, [questions]);
const handleAnswareChange = (e, index) => {
dispatch({
type: "answer",
QuestionsID: currentQuestions,
optionIndex: index,
value: e.target.checked,
});
};
return (
<>
{loading && <div>Loading.....</div>}
{error && <div>There was a problem.</div>}
{!loading && !error && qna && qna.length > 0 && (
<>
<h1>{qna[currentQuestions].title}</h1>
<h4>Question can have multiple answers</h4>
<Answare
options={qna[currentQuestions].options}
handleChange={handleAnswareChange}
/>
<PogressBar />
<MiniPlayer />
</>
)}
</>
);
}
export default Quiz;
At my point of view you should console out e.target.checked before dispatching it. it may be undefined

React Native Undefined is not an object (evaluating 'userData.id)

I'm new to react native, I have a personal project, I am trying to get data from Firestore cloud, but I keep getting this error on the screen change.
It works fine when I comment out the database code, so I'm wondering what could be the cause.
My code
import React from "react";
import auth from "#react-native-firebase/auth";
import firestore from "#react-native-firebase/firestore";
const ProfileStackScreen = ({ navigation }) => {
React.useEffect(() => {
const usr = auth().currentUser;
setuserData(prev => {
return { ...prev, uid: usr.uid };
});
}, []);
const userRef = firestore().collection("users");
const snapshot = userRef
.where("uid", "==", userData.uid)
.onSnapshot()
.then(console.log(uid))
.catch(error => {
Alert.alert(error.message);
});
const [userData, setuserData] = React.useState({
uid: ""
// other field go here
});
return (
<View>
<Text>{userData.uid}</Text>
</View>
);
};
export default ProfileStackScreen;
You can try below code
import React from 'react';
import auth from '#react-native-firebase/auth';
import firestore from '#react-native-firebase/firestore';
const ProfileStackScreen = ({ navigation }) => {
React.useEffect(() => {
const usr = auth().currentUser;
setuserData((prev)=>{
return {...prev,uid: usr.uid};
});
}, []);
React.useEffect(() => {
fetchdata()
}, [userData]);// Once userData value has been updated then only call fetchData()
const fetchdata = ()=>{
const userRef = firestore().collection('users').doc(userData.uid).get()
.then(function (doc) {
if (doc.exists) {
console.log("Document found!");
console.log(doc.data())
} else {
console.log("No such document!");
}
});
}
const [userData, setuserData] = React.useState({
uid: '',
// other field go here
});
return (
<View>
<Text>{userData.uid}</Text>
</View>
);
};
export default ProfileStackScreen;
#Maheshvirus is right. But I think you have tried to fetch data when userData.uid is not empty.
Try this way if looking for such a way.
import React from 'react';
import auth from '#react-native-firebase/auth';
import firestore from '#react-native-firebase/firestore';
const ProfileStackScreen = ({ navigation }) => {
React.useEffect(() => {
const usr = auth().currentUser;
setuserData((prev)=> {
return {...prev,uid: usr.uid};
});
}, []);
React.useEffect(() => {
if(userData.uid !== ''){
getData()
}
}, [userData]);
const getData = () => {
firestore()
.collection('users');
.where('uid', '==', userData.uid)
.onSnapshot()
.then(() => {
console.log(uid)
})
.catch((error)=> {
Alert.alert(error.message);
});
}
const [userData, setuserData] = React.useState({
uid: '',
// other field go here
});
return (
<View>
<Text>{userData.uid}</Text>
</View>
);
};
export default ProfileStackScreen;

Categories