Problems with data flow in redux-toolkit, react - javascript
In my productSlice, createProduct loads the product into the database, but often remains in the pending state.
It is similar with deleteProduct, it deletes, but remains in the pending state.
The getProduct renders always the data of the previous product.
After setting logger middleWare into my store, I have now more information about my crud - createProduct sometimes is running without problems. But then I have in action: product/create/fulfilled and directly after that:
error state.product.push is not a function
So it not jumps into next state.
The same happens with deleteProduct - action: product/delete/fulfilled and directly after that:
error state.product.filter is not a function
getProduct - prevState, action and next state have the correct data, but the text values are obviously rendered before getProduct starts
redux-slice:
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit'
import { RootState } from '../../app/store'
import productsService from './productsService'
import {UpdateProductData} from '../../pages/ProductEdit'
export interface Product{
_id?:string,
id?:string,
image:string,
title:string,
producer:string,
categories:string[],
desc:string,
price:string,
currency:string,
colors:string[],
sizes:string[],
inStock:boolean,
createdAt:Date,
updatedAt?:Date,
accessToken?:string,
}
export interface InitialState{
product:Product[],
allProducts:Product[],
isLoading:boolean,
isSuccess:boolean,
isError:boolean,
message:string,
}
const initialState: InitialState ={
product:[],
allProducts:[],
isLoading:false,
isSuccess:false,
isError:false,
message:"",
}
type AsyncThunkConfig = {
state:RootState
}
export const createProduct = createAsyncThunk<Product, FormData, AsyncThunkConfig>('/product/create', async (productData, thunkAPI) => {
try{
const token:string = thunkAPI.getState().auth.user!.accessToken;
return await productsService.createProduct(productData, token);
} catch(error:any){
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message as string)
}
})
export const updateProduct = createAsyncThunk<Product[], UpdateProductData, AsyncThunkConfig>('/product/update', async (updateProductData, thunkAPI)=>{
try{
const token:string = thunkAPI.getState().auth.user!.accessToken;
return await productsService.updateProduct(updateProductData, token);
} catch(error:any){
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message as string)
}
})
export const deleteProduct = createAsyncThunk<Product, string, AsyncThunkConfig>('product/delete', async (Id, thunkAPI)=>{
try{
const token = thunkAPI.getState().auth.user!.accessToken;
return await productsService.deleteProduct(Id, token);
} catch(error:any){
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message as string)
}
})
export const getProduct = createAsyncThunk<Product[], string, AsyncThunkConfig>('product/find', async (Id, thunkAPI)=>{
try{
return await productsService.getProduct(Id);
} catch(error:any){
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message as string)
}
})
export const getAllProducts = createAsyncThunk<Product[], void, AsyncThunkConfig>('/product/findAll', async (_, thunkAPI)=>{
try{
return await productsService.getAllProducts();
} catch(error:any){
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message as string)
}
})
export const productsSlice = createSlice({
name:"products",
initialState,
reducers:{
reset:(state)=>initialState,
},
extraReducers(builder) {
builder
.addCase(createProduct.pending, (state)=>{
state.isLoading = true;
})
.addCase(createProduct.fulfilled, (state, action)=>{
state.isLoading = false;
state.isSuccess = true;
state.product.push(action.payload);
})
.addCase(createProduct.rejected, (state,action:any)=>{
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(updateProduct.pending, (state)=>{
state.isLoading = true;
})
.addCase(updateProduct.fulfilled, (state, action)=>{
state.isLoading = false;
state.isSuccess = true;
state.product = action.payload;
})
.addCase(updateProduct.rejected, (state, action:any)=>{
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(deleteProduct.pending, (state)=>{
state.isLoading = true;
})
.addCase(deleteProduct.fulfilled, (state, action)=>{
state.isLoading = false;
state.isSuccess = true;
state.product = state.product.filter((item)=>item._id !== action.payload.id);
})
.addCase(deleteProduct.rejected, (state, action:any)=>{
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(getProduct.pending, (state)=>{
state.isLoading = true;
})
.addCase(getProduct.fulfilled, (state, action)=>{
state.isLoading = false;
state.isSuccess = true;
state.product = action.payload;
})
.addCase(getProduct.rejected, (state, action:any)=>{
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(getAllProducts.pending, (state)=>{
state.isLoading = true;
})
.addCase(getAllProducts.fulfilled, (state, action)=>{
state.isLoading = false;
state.isSuccess = true;
state.allProducts = action.payload;
})
.addCase(getAllProducts.rejected, (state, action:any)=>{
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
}
})
export const {reset} = productsSlice.actions;
export default productsSlice.reducer;
redux-service:
import axios from 'axios';
import { UpdateProductData } from '../../pages/ProductEdit';
const API_URL = 'http://localhost:5001/api/products/';
const createProduct = async (productData:FormData, token:string)=>{
const config = {
headers:{
'Content-Type':"multipart/form-data",
token: `Bearer ${token}`,
}
}
const response = await axios.post(API_URL, productData, config);
console.log(response.data);//immediatly correct data
return response.data;
}
const updateProduct = async (updateProductData:UpdateProductData, token:string)=>{
const config ={
headers:{
'Content-Type':"multipart/form-data",
token:`Bearer ${token}`
}
}
const response = await axios.put(API_URL+updateProductData.id, updateProductData.productData, config);
console.log(response.data);
return response.data;
}
const deleteProduct = async (id:string, token:string)=>{
const config = {
headers:{
token:`Bearer ${token}`
}
}
const response = await axios.delete(API_URL + id, config);
console.log(response.data);
return response.data;
}
const getProduct = async (id:string)=>{
const getUrl = `find/${id}`;
const response = await axios.get(API_URL+getUrl);
console.log(response.data)
return response.data;
}
const getAllProducts = async ()=>{
const response = await axios.get(API_URL+ 'find');
console.log(response.data);
return response.data;
}
const productsService = {
createProduct,
updateProduct,
deleteProduct,
getProduct,
getAllProducts
}
export default productsService
store:
import { configureStore, ThunkAction, Action} from '#reduxjs/toolkit';
import authReducer from '../features/authSlice';
import userReducer from '../features/user/userSlice'
import productReducer from '../features/products/productsSlice';
import descriptionItemReducer from '../features/descriptionItems/descriptionItemSlice';
import sliderItemsReducer from '../features/sliderItems/sliderItemSlice';
import storage from 'redux-persist/lib/storage'
import {persistReducer} from 'redux-persist'
const persistConfig = {
key: 'root',
version: 1,
storage,
}
const persistedUserReducer = persistReducer(persistConfig,userReducer)
const persistedProductReducer = persistReducer(persistConfig, productReducer)
const persistedSliderReducer = persistReducer(persistConfig, sliderItemsReducer)
export const store = configureStore({
reducer: {
auth:authReducer,
user:persistedUserReducer,
products:persistedProductReducer,
descriptionItem:descriptionItemReducer,
sliderItems:persistedSliderReducer,
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
I add the code from the backend:
import {Router, Request, Response} from 'express';
const productsRouter = Router();
import {verifyTokenAndAdmin} from '../middleware/jwtVerify';
import Products from '../models/products';
import upload from '../utils/multer';
const cloudinary = require('../utils/cloudinary');
import path from 'path'
productsRouter.post('/', upload.single('image'), verifyTokenAndAdmin, async (req:Request, res:Response)=>{
console.log(req.file);
console.log(req.body);
let fileUrl = req.file!.path.replace(/\\/g, "/");
console.log(fileUrl);
try{
const uploadResult = await cloudinary.uploader.upload(fileUrl, {
upload_preset: "webshop_ts_mern",
resource_type: "auto",
})
const newProducts = new Products({
cloudinary_id: uploadResult.public_id,
title: req.body.title,
producer: req.body.producer,
categories: JSON.parse(req.body.categories).split(' '),
desc: req.body.desc,
price: req.body.price,
currency:req.body.currency,
colors:JSON.parse(req.body.colors).split(' '),
sizes: JSON.parse(req.body.sizes).split(' '),
inStock: req.body.inStock,
image: uploadResult.secure_url,
})
console.log(newProducts);
const savedproducts = await newProducts.save();
res.status(200).json(savedproducts);
} catch(error){
res.status(403)
console.log(error);
throw new Error("Action failed");
}
});
//update
productsRouter.put('/:id',upload.single("image"), verifyTokenAndAdmin, async (req:Request, res:Response)=>{
console.log(req.file);
console.log(req.body)
try{
let updatedProducts = await Products.findById(req.params.id);
if(req.file){
await cloudinary.uploader.destroy(updatedProducts?.cloudinary_id);
}
let result;
if(req.file){
let fileUrl = req.file!.path.replace(/\\/g, "/");
result = await cloudinary.uploader.upload(fileUrl, {
upload_preset: "webshop_ts_mern",
resource_type: "auto",
})
}
const updatedData = {
title: req.body.title || updatedProducts!.title,
producer: req.body.producer || updatedProducts!.producer,
categories: JSON.parse(req.body.categories) || updatedProducts!.categories,
desc: req.body.desc || updatedProducts!.desc,
price: req.body.price || updatedProducts!.price,
currency: req.body.currency || updatedProducts!.currency,
colors: JSON.parse(req.body.colors) || updatedProducts!.colors,
sizes: JSON.parse(req.body.sizes) || updatedProducts!.sizes,
inStock: req.body.inStock || updatedProducts!.inStock,
cloudinary_id: result ? result.public_id : updatedProducts!.cloudinary_id,
image: result ? result.secure_url : updatedProducts!.image,
}
console.log(updatedData);
updatedProducts = await Products.findByIdAndUpdate(req.params.id, updatedData, {
new:true,
})
res.status(200).json(updatedProducts);
} catch(error){
res.status(404)
console.log(error);
throw new Error('Not found')
}
});
//delete
productsRouter.delete('/:id', verifyTokenAndAdmin, async (req:Request, res:Response)=>{
try{
let deleteProducts = await Products.findById(req.params.id);
await cloudinary.uploader.destroy(deleteProducts!.cloudinary_id);
await deleteProducts!.remove();
res.status(200).json("Produkt wurde gelöscht");
} catch(error){
res.status(404)
throw new Error("Nicht gefunden")
}
});
//get
productsRouter.get('/find/:id', async (req:Request, res:Response)=>{
try{
const products = await Products.findById(req.params.id);
res.status(200).json(products)
} catch(error){
res.status(404)
throw new Error("Nicht gefunden");
}
});
//get All
productsRouter.get('/find/', async (req:Request, res:Response)=>{
try{
const allProducts = await Products.find()
res.status(200).json(allProducts);
} catch(error){
res.status(404)
throw new Error("Not found");
}
})
export default productsRouter;
In your update and delete controllers you have written optional operator incorrectly, you have written !. instead it should be ?.. I have updated the code for you.
//update
productsRouter.put('/:id',upload.single("image"), verifyTokenAndAdmin, async (req:Request, res:Response)=>{
try{
let updatedProducts = await Products.findById(req.params.id);
if(req.file){
await cloudinary.uploader.destroy(updatedProducts?.cloudinary_id);
}
let result;
if(req.file){
let fileUrl = req.file?.path.replace(/\\/g, "/");
result = await cloudinary.uploader.upload(fileUrl, {
upload_preset: "webshop_ts_mern",
resource_type: "auto",
})
}
const updatedData = {
title: req.body.title || updatedProducts?.title,
producer: req.body.producer || updatedProducts?.producer,
categories: JSON.parse(req.body.categories) || updatedProducts?.categories,
desc: req.body.desc || updatedProducts?.desc,
price: req.body.price || updatedProducts?.price,
currency: req.body.currency || updatedProducts?.currency,
colors: JSON.parse(req.body.colors) || updatedProducts?.colors,
sizes: JSON.parse(req.body.sizes) || updatedProducts?.sizes,
inStock: req.body.inStock || updatedProducts?.inStock,
cloudinary_id: result ? result.public_id : updatedProducts?.cloudinary_id,
image: result ? result.secure_url : updatedProducts?.image,
}
console.log(updatedData);
updatedProducts = await Products.findByIdAndUpdate(req.params.id, updatedData, {
new:true,
})
res.status(200).json(updatedProducts);
} catch(error){
res.status(404)
console.log(error);
throw new Error('Not found')
}
});
//delete
productsRouter.delete('/:id', verifyTokenAndAdmin, async (req:Request, res:Response)=>{
try{
let deleteProducts = await Products.findById(req.params.id);
await cloudinary.uploader.destroy(deleteProducts?.cloudinary_id);
await deleteProducts?.remove();
res.status(200).json("Produkt wurde gelöscht");
} catch(error){
res.status(404)
throw new Error("Nicht gefunden")
}
});
Related
http://localhost:9000/api/users net::ERR_CONNECTION_REFUSED and jwt must be provided
I am creating a blog application. In every parameterized route I get the details of that specific user.For eg: /profile/#randomuser get the details of #randomusers, /profile/#robert get the details of #robert. I get the details of parameterized toute users but not the user who log in to the blog application. Everything is imported correctly. <Route path="/profile/:profile" element={\<Profile /\>} Profile.jsx const [user, getUser] = useState([]) const [glbUser, setUser] = useContext(UserContext) const match = useParams().profile const match = useMatch('/profile/:profile') const userMatch = match.params.profile console.log(" usematchis ", userMatch) const userParams = useParams().profile useEffect(() => { async function fetchGlbUser() { const loggedInUser = window.localStorage.getItem('userToken') if (loggedInUser) { const user = JSON.parse(loggedInUser) loginServices.setToken(user.token) // loginServices.createToken() const config = { headers: { Authorization: user.token } } const glbUser = await axios.get("${postUrl}", config) setUser(glbUser.data.glbUserToken) return glbUser.data.glbUserToken } } fetchGlbUser() }, []) // console.log("Match is", match) // console.log("Type of Match is", typeof match.params.profile) useEffect(() =\> { axios.get("http://localhost:9000/api/users/${userMatch}", { params: { profile: userMatch } }) .then(res =\> { console.log(res.data) getUser(res.data) // getUser(res.data.newUser) }) .catch(err => console.log(err)) // const getUserData = async () => { // const res = loginServices.getProfile(`http://localhost:9000/api/users/${userMatch}`, {params:{profile:userMatch}}) // return res.data // } // getUserData() }, []) Backend.js router.get('/:profile', async (req, res) =\> { console.log(req) const username = req.params.profile const decodedToken = jwt.verify(getToken(req), process.env.SECRET_KEY) // console.log(username) // console.log(typeof username) try { const newUser = await User.findOne({ username }) const glbUserToken = await User.findById(decodedToken.id) // console.log(newUser) res.status(200).json({ newUser, glbUserToken }) } catch (err) { console.log(err) } }) const getToken = req => const auth = req.get('authorization') if (auth && auth.startsWith(`bearer `)) return auth.replace('bearer ', '') } return null }
Redux actions always in pending state
I am trying to create a scraping application using redux toolkit for learning purposes.Whenever I dispatch the action the data gets scraped and console logged but the action state is never fullfilled and is always pending MY ASYNC THUNK export const loadData = createAsyncThunk( "alldata/getdata", async ({ pageNo, language }, thunkAPI) => { const data = await fetch( `http://localhost:5000/scrape?pageNo=${encodeURIComponent( pageNo )}&language=${encodeURIComponent(language)}` ); const res=await data.json() return { payload: res, }; } ); MY SLICE const projectSlice = createSlice({ name: "allprojects", initialState: { projectState: [], workingState: [], isLoading: false, hasError: false, }, reducers: { addProject: (state, action) => { return state.workingState.push(action.payload); }, removeProject: (state, action) => { return state.workingState.filter( (project) => project.link !== action.payload.link ); }, }, extraReducers: { [loadData.pending]: (state, action) => { state.isLoading = true; state.hasError = false; }, [loadData.fulfilled]: (state, { payload }) => { state.projectState = payload; state.isLoading = false; state.hasError = false; }, [loadData.rejected]: (state, action) => { state.isLoading = false; state.hasError = true; }, }, }); export const { addProject, removeProject } = projectSlice.actions; const Projectreducer = projectSlice.reducer; export default Projectreducer; export const projectSelector = (state) => state.allprojects; REACT COMPONENT const { workingState, projectState, isLoading, hasError } = useSelector(projectSelector); const dispatch = useDispatch(); const [selectData, setSelectData] = React.useState({ languages: "" }); const [pageData, setPageData] = React.useState({ pageNo: 1 }); const handleClick = (event) => { event.preventDefault(); dispatch( loadData({ pageNo: pageData.pageNo, language: selectData.languages }) ); }; So how do I get the action to be fullfilled and push the data in the ProjectState array after the async request EDIT: API app.get("/scrape", async (req, res) => { const { pageNo, language } = req.query; const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.goto( `https://github.com/search?p=${pageNo}&q=language%3A${language}`, { waitUntil: "domcontentloaded", } ); // URL is given by the "user" (your client-side application) const data = await page.evaluate(() => { const list = []; const items = document.querySelectorAll(".repo-list-item"); for (const item of items) { list.push({ projectName: item.querySelector(".f4 > a").innerText, about: item.querySelector("p").innerText, link: item.querySelector("a").getAttribute("href"), }); } return list; }); console.log(data); await browser.close(); }); Store import { configureStore } from "#reduxjs/toolkit"; import Projectreducer from "./Slices/slice"; export const store = configureStore({ reducer: { allprojects: Projectreducer, }, });
Its possible that the api that you are fetching is throwing an error so in this case it always recommended to have a catch block and throw an error to that its falls into loadData.rejected state. So, do the check network tab in the dev tools of the browser that you are using, so that you can confirm if the api is responding or not. Also can you share the projectSelector selector ? could be the something wrong in the selector. action: export const loadData = createAsyncThunk( 'alldata/getdata', async ({ pageNo, language }, { rejectWithValue }) => { try { const data = await fetch( `http://localhost:5000/scrape?pageNo=${encodeURIComponent( pageNo )}&language=${encodeURIComponent(language)}` ); const res = await data.json(); return { payload: res, }; } catch (error) { return rejectWithValue({ payload: error?.message || error }); } } ); reducer: extraReducers: { ..., [loadData.rejected]: (state, { payload }) => { state.isLoading = false; state.hasError = true; state.message = payload; }, }, One more thing to mention here is that redux toolkit recommends to use builder callback, for more details check here: https://redux-toolkit.js.org/api/createslice#extrareducers in your example it would be like extraReducers: (builder) => { builder .addCase(loadData.pending, (state) => { ... }) .addCase(loadData.fulfilled, (state, action) => { ... }) .addCase(loadData.rejected, (state, action) => { ... });
Cannot read property of undefined data() Firebase
I'm trying to implement redux saga in my old project and i get strange error. I'm getting back the userData with all user details but my middleware logger tells me that sign_in_failed fires instead of sign_in_success with this error: Cannot read property of undefined data() Firebase. So i tried to do optional chaining and i also tried the old way of deep property check (with &&) but this didnt help. Also, i cant anymore perform a stripe payments due to this error because stripe cant see the user. UserSaga.jsx import { takeLatest, all, call, put } from 'redux-saga/effects' import { USER_ACTIONS_TYPES } from '../../actions/user/userActionsTypes' import { signInSuccess, signInFailed, signUpSuccess, signUpFailed, signOutSuccess, signOutFailed } from '../../actions/user/userActionCreator' import { getCurrentUser, createUserDocumentFromAuth, createAuthUserWithEmailAndPassword, signInWithGooglePopup, signInAuthUserWithEmailAndPassword, signOutAuthUser } from '../../../database/firebase.config' export function* getUserSnapshot(userAuth, additionalInformation) { try { const userSnapshot = yield call( createUserDocumentFromAuth, userAuth, additionalInformation ); //yield put(signInSuccess({ id: userSnapshot?.id, ...userSnapshot.data()})); // console.log('userSnapshot2>>>>>>>>>', ...userSnapshot.data()) const id = userSnapshot?.id; const userData = userSnapshot.data() const {createdAt, email, password}= userData yield put(signInSuccess({ id, createdAt, email, password })); console.log('userSnapshot2>>>>>>>>>', userSnapshot) } catch (error) { yield put(signInFailed(error.message)) } } export function* signInWithGoogle() { try { const { user } = yield call(signInWithGooglePopup) yield call(getUserSnapshot, user) } catch (error) { yield put(signInFailed(error)) } } export function* signInWIthEmail({ payload: { email, password } }) { try { const { user } = yield call(signInAuthUserWithEmailAndPassword, email, password) yield call(getUserSnapshot, user) } catch (error) { yield put(signInFailed(error)) } } export function* isUserAuth() { try { const userAuth = yield call(getCurrentUser) if (!userAuth) return; yield call(getUserSnapshot, userAuth) } catch (error) { yield put(signInFailed(error)) } } export function* signUpWIthEmail({ payload: { email, password, displayName } }) { try { const { user } = yield call(createAuthUserWithEmailAndPassword, email, password) yield put(signUpSuccess(user, { displayName })) } catch (error) { yield put(signUpFailed(error)) } } export function* signOut() { try { yield call(signOutAuthUser) yield put(signOutSuccess()) } catch (error) { yield put(signOutFailed(error)) } } export function* signInAfterSignUp({ payload: { user, additionalDetails } }) { yield call(getUserSnapshot, user, additionalDetails) } export function* onGoogleSignInStart() { yield takeLatest(USER_ACTIONS_TYPES.GOOGLE_SIGN_IN_START, signInWithGoogle) } export function* onCheckUserSession() { yield takeLatest(USER_ACTIONS_TYPES.CHECK_USER_SESSION, isUserAuth) } export function* onEmailSignInStart() { yield takeLatest(USER_ACTIONS_TYPES.EMAIL_SIGN_IN_START, signInWIthEmail) } export function* onSignUpStart() { yield takeLatest(USER_ACTIONS_TYPES.SIGN_UP_START, signUpWIthEmail) } export function* onSignUpSuccess() { yield takeLatest(USER_ACTIONS_TYPES.SIGN_IN_SUCCESS, signInAfterSignUp) } export function* onSignOutStart() { yield takeLatest(USER_ACTIONS_TYPES.SIGN_OUT_START, signOut) } export function* userSagas() { yield all([ call(onCheckUserSession), call(onGoogleSignInStart), call(onEmailSignInStart), call(onSignUpStart), call(onSignUpSuccess), call(onSignOutStart), call(onSignUpSuccess) ]) } firebase.config.js // Initialize Firebase and Oauth initializeApp(firebaseConfig); const googleProvider = new GoogleAuthProvider(); googleProvider.setCustomParameters({ prompt: "select_account" }) //Initialize GAuth with popup signin method export const auth = getAuth() export const signInWithGooglePopup = () => signInWithPopup(auth, googleProvider) // export const signInWithGoogleRedirect = () => signInWithRedirect(auth, googleProvider) // Initialize Firestore export const db = getFirestore() // get the conllection created with all docs export const getCollectionWithDocuments = async() => { const collectionRef = collection(db, 'categories') const q = query(collectionRef) const querySnapShot = await getDocs(q) return querySnapShot.docs.map(docSnapshot => docSnapshot.data()) } //Create a user document export const createUserDocumentFromAuth = async(userAuth, additionalInformation = {}) => { if (!userAuth) return; const userDocRef = doc(db, 'users', userAuth.uid) // console.log('userRef>>', userDocRef) const userSnapshot = await getDoc(userDocRef) // console.log('just snapshot>>>', snapShot) // console.log('if shot exist>>', snapShot.exists()) if (!userSnapshot.exists()) { const { displayName, email } = userAuth const createdAt = new Date(); try { await setDoc(userDocRef, { displayName, email, createdAt, ...additionalInformation }) } catch (error) { console.log("error creating user", error.message) } } return userSnapshot; } export const createAuthUserWithEmailAndPassword = async(email, password) => { if (!email || !password) return; return await createUserWithEmailAndPassword(auth, email, password) } export const signInAuthUserWithEmailAndPassword = async(email, password) => { if (!email || !password) return; return await signInWithEmailAndPassword(auth, email, password) } export const signOutAuthUser = async() => await signOut(auth) export const onAuthObserver = (callback) => onAuthStateChanged(auth, callback) //Create a collection of document in firestore (call it ones inside useeffect to avoid mutations) export const createCollectionWithDocuments = async(collectionName, objectsToAdd) => { const collectionRef = collection(db, collectionName) const batch = writeBatch(db) objectsToAdd.forEach(object => { const docRef = doc(collectionRef, object.title.toLowerCase()) batch.set(docRef, object) }) await batch.commit() console.log('collection created') } export const getCurrentUser = () => { return new Promise((resolve, reject) => { const unsubscribe = onAuthStateChanged( auth, (userAuth) => { unsubscribe(); resolve(userAuth); // console.log(userAuth) }, reject) }) } userReducer.jsx export const INITIAL_STATE = { currentUser: null, isLoading: false, error: null } export const userReducer = (state = INITIAL_STATE, action) => { const { type, payload } = action; switch (type) { case USER_ACTIONS_TYPES.SIGN_IN_SUCCESS: return { ...state, currentUser: payload, error: null } case USER_ACTIONS_TYPES.SIGN_OUT_SUCCESS: return { ...state, currentUser: null } case USER_ACTIONS_TYPES.SIGN_OUT_FAILED: case USER_ACTIONS_TYPES.SIGN_UP_FAILED: case USER_ACTIONS_TYPES.SIGN_IN_FAILED: return { ...state, error: payload } default: return state } } userActions.js export const emailSignInStart = (email, password) => createActions(USER_ACTIONS_TYPES.EMAIL_SIGN_IN_START, { email, password }) export const signInSuccess = user => createActions(USER_ACTIONS_TYPES.SIGN_IN_SUCCESS, user) export const signInFailed = error => createActions(USER_ACTIONS_TYPES.SIGN_IN_FAILED, error)
Uncaught ReferenceError: keyword is not defined at UseFetch.jsx and at Transactions.jsx
I am trying to load a website to my localhost but keep running into an error that says Uncaught ReferenceError: keyword is not defined at useFetch (:3000/src/hooks/useFetch.jsx:21:7) at TransactionsCard (:3000/src/components/Transactions.jsx:33:18). The issue hapens in my code where I'm fetching gifs from my API at giphy developer. Here is the source code: Transactions.jsx: import React, { useEffect, useState } from "react"; import { ethers } from "ethers"; import { contractABI, contractAddress } from "../utils/constants"; export const TransactionContext = React.createContext(); const { ethereum } = window; const createEthereumContract = () => { const provider = new ethers.providers.Web3Provider(ethereum); const signer = provider.getSigner(); const transactionsContract = new ethers.Contract(contractAddress, contractABI, signer); return transactionsContract; }; export const TransactionsProvider = ({ children }) => { const [formData, setformData] = useState({ addressTo: "", amount: "", keyword: "", message: "" }); const [currentAccount, setCurrentAccount] = useState(""); const [isLoading, setIsLoading] = useState(false); const [transactionCount, setTransactionCount] = useState(localStorage.getItem("transactionCount")); const [transactions, setTransactions] = useState([]); const handleChange = (e, name) => { setformData((prevState) => ({ ...prevState, [name]: e.target.value })); }; const getAllTransactions = async () => { try { if (ethereum) { const transactionsContract = createEthereumContract(); const availableTransactions = await transactionsContract.getAllTransactions(); const structuredTransactions = availableTransactions.map((transaction) => ({ addressTo: transaction.receiver, addressFrom: transaction.sender, timestamp: new Date(transaction.timestamp.toNumber() * 1000).toLocaleString(), message: transaction.message, keyword: transaction.keyword, amount: parseInt(transaction.amount._hex) / (10 ** 18) })); console.log(structuredTransactions); setTransactions(structuredTransactions); } else { console.log("Ethereum is not present"); } } catch (error) { console.log(error); } }; const checkIfWalletIsConnect = async () => { try { if (!ethereum) return alert("Please install MetaMask."); const accounts = await ethereum.request({ method: "eth_accounts" }); if (accounts.length) { setCurrentAccount(accounts[0]); getAllTransactions(); } else { console.log("No accounts found"); } } catch (error) { console.log(error); } }; const checkIfTransactionsExists = async () => { try { if (ethereum) { const transactionsContract = createEthereumContract(); const currentTransactionCount = await transactionsContract.getTransactionCount(); window.localStorage.setItem("transactionCount", currentTransactionCount); } } catch (error) { console.log(error); throw new Error("No ethereum object"); } }; const connectWallet = async () => { try { if (!ethereum) return alert("Please install MetaMask."); const accounts = await ethereum.request({ method: "eth_requestAccounts", }); setCurrentAccount(accounts[0]); window.location.reload(); } catch (error) { console.log(error); throw new Error("No ethereum object"); } }; const sendTransaction = async () => { try { if (ethereum) { const { addressTo, amount, keyword, message } = formData; const transactionsContract = createEthereumContract(); const parsedAmount = ethers.utils.parseEther(amount); await ethereum.request({ method: "eth_sendTransaction", params: [{ from: currentAccount, to: addressTo, gas: "0x5208", //21,000 gwei in hexadecimal form value: parsedAmount._hex, }], }); const transactionHash = await transactionsContract.addToBlockchain(addressTo, parsedAmount, message, keyword); setIsLoading(true); console.log(`Loading - ${transactionHash.hash}`); await transactionHash.wait(); console.log(`Success - ${transactionHash.hash}`); setIsLoading(false); const transactionsCount = await transactionsContract.getTransactionCount(); setTransactionCount(transactionsCount.toNumber()); window.location.reload(); } else { console.log("No ethereum object"); } } catch (error) { console.log(error); throw new Error("No ethereum object"); } }; useEffect(() => { checkIfWalletIsConnect(); checkIfTransactionsExists(); }, [transactionCount]); return ( <TransactionContext.Provider value={{ transactionCount, connectWallet, transactions, currentAccount, isLoading, sendTransaction, handleChange, formData, }} > {children} </TransactionContext.Provider> ); }; useFetch.jsx: import { useEffect, useState } from 'react'; const API_KEY = import.meta.env.VITE_GIPHY_API; const useFetch = () => { const [gifUrl, setGifUrl] = useState(""); const fetchGifs = async () => { try { const response = await fetch(`https://api.giphy.com/v1/gifs/search?api_key=${API_KEY}&q=${keyword.split(" ").join("")}&limit=1`) const { data } = await response.json(); setGifUrl(data[0]?.images?.downsized_medium?.url) } catch (error) { setGifUrl('https://metro.co.uk/wp-content/uploads/2015/05/pokemon_crying.gif?quality=90&strip=all&zoom=1&resize=500%2C284') } } useEffect(() => { if (keyword) fetchGifs(); }, [keyword]); return gifUrl; } export default useFetch; When I comment out the lines that use 'keyword' it launches with no errors. This is at lines 14, 18, and 58-62 of Transactions.jsx. Any help would be greatly appreciated, thank you!
The problem here is that you have not define keyword inside your useFetch function. If you are trying to pass the keyword from the place where you use useFetch then do something like below and use the useFetch like const gifUrl = useFetch(<keyword>) import { useEffect, useState } from 'react'; const API_KEY = import.meta.env.VITE_GIPHY_API; const useFetch = (keyword) => { const [gifUrl, setGifUrl] = useState(""); const fetchGifs = async () => { try { const response = await fetch(`https://api.giphy.com/v1/gifs/search?api_key=${API_KEY}&q=${keyword.split(" ").join("")}&limit=1`) const { data } = await response.json(); setGifUrl(data[0]?.images?.downsized_medium?.url) } catch (error) { setGifUrl('https://metro.co.uk/wp-content/uploads/2015/05/pokemon_crying.gif?quality=90&strip=all&zoom=1&resize=500%2C284') } } useEffect(() => { if (keyword) fetchGifs(); }, [keyword]); return gifUrl; } export default useFetch; or even try adding default value for key work like below. const useFetch = (keyword = "some keyword") => {
Cannot read property OnSnapshot of undefined
componentDidMount() { this.authListener = auth.onAuthStateChanged( async userAuth => { if(userAuth){ const userRef = await handleUserProfile(userAuth); userRef.onSnapshot(snapshot=>{ this.setState({ currentUser:{ id:snapshot.id, ...snapshot.data() } }) }) } this.setState({ ...initialState }) }); }
Your function handleUserProfile is not returning the userRef so you get that error. It would be great to provide more that to help you more with this. Maybe the code of that function handleUserProfile. Can you try to updated your handleUserProfile function to: export const handleUserProfile = async ({ userAuth, additionalData = {} }) => { if (!userAuth) { console.warn("No userAuth provided!"); return; } const { uid } = userAuth; const userRef = firestore.doc(`users/${uid}`); const snapshot = await userRef.get(); if (!snapshot.exists) { const { displayName, email } = userAuth; const timestamp = new Date(); try { await userRef.set({ displayName, email, createdDate: timestamp, ...additionalData, }); } catch (err) { console.warn(err); } } return userRef; }; If you are getting the log No userAuth provided! you are not sending a userAuth to it.