React / useEffect doesn't call/dispatch action creator - reactjs

This should work ->
import { useEffect } from 'react'
import { useSelector, useDispatch } from "react-redux";
import CheckLoginStatus from "../../utils/CheckLoginStatus";
import ProductDetailDisplay from '../../components/product/ProductDetailDisplay'
// using #reduxjs/toolkit as Redux state / store thing
import { selectProduct, getProductById } from '../products/productsSlice'
function handleAddToCart(){
alert('handleAddToCart')
}
export default function ProductDetail ( props ) {
let { productId } = props
CheckLoginStatus()
const dispatch = useDispatch()
const productData = useSelector(selectProduct)
useEffect(() => {
console.log('useEffect()') // <<< 'useEffect()' appears in console
dispatch(getProductById(productId)); // <<< it looks like it's never called
},[])
return <ProductDetailDisplay data={productData} handleAddToCart={ handleAddToCart }/>
}
However, the 'dispatch(getProductById(productId))' inside of useEffect doesn't happen. (edit) However, the console.log('useEffect()') line does execute, and the message appears in the console.
I've been staring at this for an entire day, trying out all of the combinations of solutions I can think of, and have nothing.
Can someone please let me know where I'm missing the point with this code? It should work, and a single product record should be available to ProductDetailDisplay. But it looks like 'getProductById(productId))' never gets called.
Thanks in advance.
(edit) krirkrirk, here is the code for getProductById ->
export const getProductById = createAsyncThunk (
'products/getProductById',
async (product_id) => {
let theApiUrl = API_BASE_URL + `/api/v1/product/${product_id}`
let authToken = useSelector(selectJwtToken)
console.log('getProductById', product_id)
try {
const response = await fetch(
theApiUrl,
{
method: 'GET',
headers: {
'Accept': "*/*",
'Content-Type': "application/json",
'Authorization': `Bearer ${authToken}`,
},
credentials: 'include',
}
)
let data = await response.json();
if (response.status === 200) {
// console.log('getProductById 200', data[0])
console.log('productById ', data)
return data[0] // FIXME: shouldn't have to juggle arrays when assigning to state...
} else if (response.status === 401) {
console.log('getProductById get request auth fail.')
// return thunkAPI.rejectWithValue(data)
}
} catch (e) {
console.log("Error: ", e.response.data)
// return thunkAPI.rejectWithValue(e.response.data)
}
}
) // end getProductById

Related

Next.js - React Custom Hook throws Invalid hook call

Hi I am quite new to react and this is for a learning project.
In react under next.js want to check for the existence of a certain folder on the server. To achieve that I implemented an api twigExists.js in pages/api and a custom hook twigExistsRequest.js in the library folder:
import {useEffect, useRef} from "react";
import {webApiUrl} from "#/library/webHelpers";
export function useTwigExistsRequest({
parentDirSegment,
name,
action,
treeStateDispatch
}) {
const nameExists = useRef('not');
useEffect(() => {
if ('' !== name) {
async function fetchNameValidation() {
try {
const response = await fetch(
webApiUrl() + '/branchName',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({parentDirSegment, name})
}
);
const answer = await response.json();
if (undefined !== answer['exists']) {
nameExists.current = answer['exists'];
}
else if (undefined !== answer['err']) {
console.log(answer['err']);
}
} catch (err) {
console.log(err);
}
}
fetchNameValidation().then(() => {
nameExists.current === 'exists'
&& treeStateDispatch({
action,
name,
dirSegment: parentDirSegment
});
})
}
});
}
The following error is thrown at the useRef line, line 10:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component.
I am using an almost identical approach to get the structure of a special folder with its subfolders and it is working fine. Working example:
import {useEffect, useRef} from "react";
import {webApiUrl} from "#/library/webHelpers";
export default function useChangeBranchRequest({
data,
setData
}) {
let postData;
const storeEffect = useRef(0);
if ('skip' !== data) {
storeEffect.current += 1;
postData = JSON.stringify(data);
}
useEffect(() => {
if (0 !== storeEffect.current) {
async function fetchData() {
try {
const response = await fetch(
webApiUrl() + '/changeBranch',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: postData
});
const json = await response.json();
setData(JSON.parse(json['tree']));
} catch (error) {
console.log(error);
}
}
fetchData()
.then(() => {
return (<></>);
});
}
}, [storeEffect.current]);
}
I can't see: What is wrong in the first example??
Edit due to question: useTwigExistsRequest is called from this file:
import {useTwigExistsRequest} from "#/library/twigExistsRequest";
export default function twigExistsHandler({
parentDirSegment,
name,
action,
treeStateDispatch
}) {
useTwigExistsRequest({
parentDirSegment,
action,
name,
treeStateDispatch
});
}
trying to avoid a direct call from:
import {ACTIONS} from "#/library/common";
import {useState} from "react";
import twigExistsHandler from "#/library/twigExistsHandler";
export default function PlgButton({dirSegment, action, text, treeStateDispatch}) {
const [info, SetInfo] = useState('');
const [parentDirSegment, SetParentDirSegment] = useState('');
// name validation, triggered by SetInfo. Returns strings 'false' or 'true':
// reset Button after execution
if (info) SetInfo('');
return (
<button
className="btn btn-secondary btn-sm new-plg-btn"
onClick={() => {
clickHandler(action);
}}
>
{text}
</button>
);
function clickHandler(action) {
let name;
switch (action) {
case ACTIONS.add:
name = window.prompt('New name:');
twigExistsHandler({
parentDirSegment: dirSegment,
name,
action,
treeStateDispatch
});
break;
case ACTIONS.dup:
name = window.prompt('Dup name:');
twigExistsHandler({
parentDirSegment: dirSegment.slice(0,dirSegment.lastIndexOf('/')),
name,
action,
treeStateDispatch
});
break;
case ACTIONS.del:
window.confirm('Irrevocably delete the whole playground?')
&& treeStateDispatch({
info: '',
dirSegment,
action
});
break;
}
}
}

Value of props is undefined when accessed inside a function

I have a function which update and add new record to my database. I have passed props called matterId from parent to child component but if I do console log inside the functions, it shows undefined instead.
import React, { useState, useEffect } from 'react';
import { Table, Button, Modal, Form } from 'react-bootstrap';
export default function TimeEntry(props){
const { matterId, timeEntriesData } = props;
console.log(`matterId: ${matterId}`)
const [timeEntries, setTimeEntries] = useState([]);
const addTimeEntry = (e, matterId) => {
console.log(`matterId: ${matterId}`)
e.preventDefault();
fetch(`http://localhost:4000/matters/628607f1c8a4009f2fd4801e/timeEntry`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${ localStorage.getItem('token') }`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
description: entryDescription
})
})
.then(res => res.json())
.then(data => {
if (data === true) {
// fetchData();
alert("New time entry successfully aded.");
closeEdit();
} else {
alert("Something went wrong.");
closeEdit();
}
});
};
};
console.log shows this: matterId: undefined
You are declaring the variable again in the function signature.
Change it something like this
const addTimeEntry = (e) => {
console.log(`matterId: ${matterId}`)
....
}
const { matterId, timeEntriesData } = props;
// This matterId is from props.
console.log(`matterId: ${matterId}`);
const [timeEntries, setTimeEntries] = useState([]);
const addTimeEntry = (e, matterId) => {
// This matterId is from function addTimeEntry
console.log(`functionMatterId: ${matterId}`);
};
// Call function like this
addTimeEntry(e, "id"); // expected console >> functionMatterId: id

react-query, Invalid hook call

I am trying to make a function to easily execute queries. Perhaps I initially did everything wrong, I will be glad if you correct me.
I created file Request.js:
import React from "react";
import { useQuery } from 'react-query'
export const Request = (method = "GET", body = {}, url = 'http://localhost:3000/notes') => {
const parameters = {
headers: {
'Content-Type': 'application/json',
},
method: method,
};
if (method == "POST" || method == "PUT") {
parameters.body = JSON.stringify(body);
}
const { status, data, isFetching, error } = useQuery("repoData", async (url, parameters) => {
return await fetch(
url, parameters
)
});
if (status === 'loading') {
return <div>loading...</div> // loading state
}
if (status === 'error') {
return <div>{error.message}</div> // error state
}
return data;
};
And in index.js i called this function
import { Request } from './Request/Request';
console.log(Request());
But i have error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

How to create a custom Hooks in reactjs hooks?

I was trying to create a custom Hooks for handling input HTTP request from any component by simply calling the useCustomHooks but its getting failed and error is
Can not use keyword 'await' outside an async function
All i made is a handler that triggers http request custom component method
import { useState } from 'react';
import axios from "axios";
const useHttpReqHandler = () => {
const [result, setResult] = useState();
const apiMethod = async ({url , data , method}) => {
let options = {
method,
url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
data
};
let response = await axios(options);
const UpdatedData = await response.data;
console.log(UpdatedData)
setResult(UpdatedData);
}
return [result, apiMethod];
};
export default useHttpReqHandler;
Now i can use this hook in my code and on any event handler just call callAPI returned from the hook like this
const MyFunc = () => {
const [apiResult, apiMethod] = useHttpReqHandler();
const captchValidation = () => {
const x = result.toString();;
const y = inputValue.toString();;
if ( x === y) {
apiMethod({url: 'some url here', data: {"email": email}, method: 'post'});
alert("success")
}
else {
alert("fail")
}
}
Is is a correct approch ? as i am beginner in Reactjs
Here is a working version:
useHttpReqHandler.jsx
import { useState } from 'react';
import axios from "axios";
const useHttpReqHandler = () => {
const [apiResult, setApiResult] = useState();
const apiMethod = async ({url , data , method}) => {
let options = {
method,
url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
data
};
let response = await axios(options);
let responseOK = response && response.status === 200 && response.statusText === 'OK';
if (responseOK) {
const data = await response.data;
console.log(data)
setApiResult(data);
}
}
return [apiResult, apiMethod];
};
export default useHttpReqHandler;
What's important here:
await is called inside an async function (apiMethod)
The result is stored in a local state (apiResult)
The function returns an array [apiResult, apiMethod]
How to use it:
const [apiResult, apiMethod] = useHttpReqHandler();
apiMethod({url: 'some url here', data: {"email": email}, method: 'post'});
Render the result:
return {apiResult};
In my opinion, it is better to use .then with Axios. and try to create for each method different functions "Get/Post...", why because in the GET method you need to useEffect, but it can not be the same case in POST method. in GET method useHttpReqHandler.js
import { useEffect, useState } from "react";
import axios from "axios";
// GET DATA
const useHttpReqHandler = (url) => {
const [httpData, setHttpData] = useState();
useEffect(() => {
axios
.get(url)
.then((axiosData) => {
// Axios DATA object
setHttpData(axiosData.data);
// you can check what is in the object by console.log(axiosData);
// also you can change the state, call functions...
})
.catch((error) => {
console.log(error);
});
}, []);
return httpData;
};
export default useHttpReqHandler;
in your main file
import useHttpReqHandler from "...."
const MyFunc = () => {
const getData = useHttpReqHandler("URL");
return (
<div>
...
</div>
)
}
I hope it helps
the same thing will be with POSt, PUT, DELETE ... you will create functions for each method that will handle the Http req

React TypeScript: Custom Fetch Hook

I'm trying to write a custom fetch hook, but I guess im missing something.
import React, { useContext } from 'react';
import { Context } from "../components/context";
const fetchHook = async(url: string, bearer: string, method: string, body: any ) => {
const { global } = useContext(Context) as {global: any};
let headers = {'cache-control': 'no-cache', 'Content-Type': 'application/json' };
if (bearer) headers = {...headers, ...{'Authorization': bearer}}
if (method === 'GET') return await fetch(global.apiUrl + url, {method, headers});
else return await fetch(global.apiUrl + url, {method, headers, body});
}
export { fetchHook }
The error im getting is Line 5: React Hook "useContext" is called in function "fetchHook" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks
UPDATE:
import React, { useContext } from 'react';
import { Context } from "../components/context";
const useFetch = (url: string, bearer: string, method: string, body: any) => {
const { global } = useContext(Context) as {global: any};
let headers = {'cache-control': 'no-cache', 'Content-Type': 'application/json' };
if (bearer) headers = {...headers, ...{'Authorization': bearer}}
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
const apiUrl = global.apiUrl;
React.useEffect(() => {
const fetchData = async () => {
try {
let res;
if (method === 'GET') res = await fetch(apiUrl + url, {method, headers});
else res = await fetch(global.apiUrl + url, {method, headers, body});
setResponse(await res.json());
} catch (error) {
setError(error);
}
};
fetchData();
}, []);
return { response, error };
};
export { useFetch }
The only warning I get now I about a missing dependency warning but I'm not sure how to fix it. Should I be passing all the dependencies into the square brackets of useEffect()?? I'm just not sure?
Line 27: React Hook React.useEffect has missing dependencies: 'apiUrl', 'body', 'global.apiUrl', 'headers', 'method', and 'url'. Either include them or remove the dependency array
You are getting this warning because according to the Rules of hooks, a custom hook name must start with use.
As mentioned the docs of custom hooks
A custom Hook is a JavaScript function whose name starts with ”use”
and that may call other Hooks.
You won't receive the error if you rename the hook to
const useFetchHook = async(url: string, bearer: string, method: string, body: any ) => {
const { global } = useContext(Context) as {global: any};
let headers = {'cache-control': 'no-cache', 'Content-Type': 'application/json' };
if (bearer) headers = {...headers, ...{'Authorization': bearer}}
if (method === 'GET') return await fetch(global.apiUrl + url, {method, headers});
else return await fetch(global.apiUrl + url, {method, headers, body});
}
export { useFetchHook }
Also one thing you must keep in mind is that if you execute async code within the custom hook directly, it will be executed on every render. A better way is to maintain state and fetch the data within useEffect and update the state when the data is received.

Resources