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;
}
}
}
Related
I created a form for registration, and added a loader spinner. But the loader jumps quickly, it is almost imperceptible. Is it correct to add a small delay using setTimeout in this case? When call setLoader(false), the is shown again and all inputs becomes empty. The data filled by the user must not disappear. How to do this, and also so that setLoader(false) is set only after receiving a response from the api? Thanks.
import React, { useState } from "react";
import { Form } from "react-bootstrap";
import Loader from "./Loader";
import {toast} from "react-toastify";
const AdministratorSignIn = () => {
const [loader, setLoader] = useState(false)
const newAdministrator = async (e) => {
e.preventDefault()
setLoader(true)
let response = await fetch('/api/v1.0/sign-up/', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'email': e.target.email.value,
'first_name': e.target.first_name.value,
'last_name': e.target.last_name.value
}
)
})
if (response.status === 201) {
toast("Success", {type: "success"})
setTimeout(() => {setLoader(false)}, 200)
}
else if (response.status === 400) {
toast("Warning"), {type: "warning"})
setTimeout(() => {setLoader(false)}, 200)
}
}
return (
{ !loader ? (
<Form onSubmit={newAdministrator}>
... some inputs here ...
</Form>
)
: (
<Loader />
)
}
)
}
export default AdministratorSignIn
I need to upload a image file to my API with Axios calls
But i keep getting a error code 500 no matter what i do?
Here is what i have done so far.
import React, { useState, useContext, useEffect } from "react";
import Axios from "axios";
// Context
import { TokenDataContext } from "../Contexts/TokenContext";
// Components
import AdminNav from "../components/admin/AdminNav";
const AddAssets = () => {
const { token } = useContext(TokenDataContext);
const header = {
headers: {
Authorization: `Bearer ${token}`,
},
};
// Create volunteer
const [assetCreated, setAssetCreated] = useState(false);
const [badAsset, setBadAsset] = useState(false);
function handleVolunteerCreateInfo(e) {
e.preventDefault();
setAssetCreated(false);
setBadAsset(false);
const form = e.target;
const file = form[0].files;
// console.log(username + ' ' + password);
Axios.post(
"http://localhost:4000/api/v1/assets",
{
file: file,
},
header
)
.then((response) => {
if (response.status === 200) {
setAssetCreated(true);
setBadAsset(false);
}
})
.catch((error) => {
setAssetCreated(false);
setBadAsset(true);
});
}
Here's what it looks like in insomnia
and here is the error
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
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
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