After a lot of research at last I found a good way to pass all my updated values of objects though components but I dont find the way to post it with axios. In my code I have a function onChange that brings the updated values from the inputs in another component (or the list of updated objects values) and a function to post, If I send a hardcoded object works just fine, but I cant find the way to sett the updated values from onChange to the actual "updateData" function (the function that make the axios post.
import React, {useState, useEffect} from 'react';
import {CustomFieldsList} from './customFieldsList';
import {toast} from 'react-toastify';
import {ToastInnerDisplay} from '#learnifier/jslib-utils';
import axios from 'axios';
export function CustomFieldsContainer({match}) {
const [value, setValue] = useState({
data: null, // <-- maybe I should pass "newList" here?
loading: true,
error: null,
});
/**
* Initial loading of data.
*/
async function fetchData() {
setValue({...value, error: null, loading: true});
try {
let url = `http://localhost:3000/projectcustomfields.json/list/1741`;
const res = await fetch(url, {
method: 'POST',
mode: 'cors',
withCredentials: true,
credentials: 'include',
});
let data = await res.json();
setValue(prevValue => ({...prevValue, data: data.customFields, loading: false}));
} catch (error) {
toast.error(<ToastInnerDisplay message={error.message} />);
setValue({...value, error, loading: false});
}
}
const updateData = async () => {
console.log(value.data);
let arrOfObj = { // <-- a "hardcoded" objects to update
"items": [
{
"id": 'party',
"value": 'value2new',
},
{
"id": 'goal',
"value": 'value2new2',
}
],
};
try {
await axios({
url: `http://localhost:3000/projectcustomfields.json/updateMany/1741`,
data: arrOfObj, // <-- right now im posting the body JSON avobe declared but I want to pass "newList" variable which I declare on onChange function
method: 'POST',
mode: 'cors',
withCredentials: true,
});
} catch (error) {
toast.error(<ToastInnerDisplay message={error.message} />);
console.log('Error when updating values: ', error);
}
};
const onChange = ({index, updatedValue}) => {
const newList = [...value.data];
const newValue = {...newList[index]};
newValue.value = updatedValue;
newList[index] = newValue;
setValue({
data: newList, //<-- this newList I want to post in upadteData()
loading: false,
error: null,
});
};
useEffect(() => {
fetchData();
}, []);
if (value.loading) {
return <div>loading...</div>;
} else if (value.error) {
return <div>ERROR</div>;
} else {
return (
<div className={'section-component'}>
<div className={'col-md-6 col-sm-12'}>
<h2>Custom Fields</h2>
<CustomFieldsList onChange={onChange} updateData={updateData} list={value.data} />
</div>
</div>
);
}
}
Any help is more than appreciated!
Fixed :) I had to map the items like this:
let arrOfObj = {
items: [...value.data],
};
let postData = [];
arrOfObj.items.map((element, index) => {
postData.push({id: element.id, value: element.value});
});
arrOfObj.items = postData;
and VOILA! doing the job
Related
So far I have a Next.js app with a form component that looks like this:
the components/Form.js
import { useState } from 'react'
import { useRouter } from 'next/router'
import { mutate } from 'swr'
const Form = ({ formId, demoForm, forNewDemo = true }) => {
const router = useRouter()
const contentType = 'application/json'
const [errors, setErrors] = useState({})
const [message, setMessage] = useState('')
const [form, setForm] = useState({
projectName: demoForm.projectName,
projectVariable: demoForm.projectVariable,
})
/* The PUT method edits an existing entry in the mongodb database. */
const putData = async (form) => {
const { id } = router.query
try {
const res = await fetch(`/api/demos/${id}`, {
method: 'PUT',
headers: {
Accept: contentType,
'Content-Type': contentType,
},
body: JSON.stringify(form),
})
// Throw error with status code in case Fetch API req failed
if (!res.ok) {
throw new Error(res.status)
}
const { data } = await res.json()
mutate(`/api/demos/${id}`, data, false) // Update the local data without a revalidation
router.push('/')
} catch (error) {
setMessage('Failed to update')
}
}
/* The POST method adds a new entry in the mongodb database. */
const postData = async (form) => {
try {
const res = await fetch('/api/demos', {
method: 'POST',
headers: {
Accept: contentType,
'Content-Type': contentType,
},
body: JSON.stringify(form),
})
// Throw error with status code in case Fetch API req failed
if (!res.ok) {
throw new Error(res.status)
}
router.push('/')
} catch (error) {
setMessage('')
}
}
const handleChange = (e) => {
const target = e.target
const value =
target.name === 'poddy_trained' ? target.checked : target.value
const name = target.name
setForm({
...form,
[name]: value,
})
}
/* Makes sure demo info is filled for demo name, owner name, species, and image url*/
const formValidate = () => {
let err = {}
if (!form.projectVariable) err.projectVariable = 'Function is required'
return err
}
const handleSubmit = (e) => {
e.preventDefault()
const errs = formValidate()
if (Object.keys(errs).length === 0) {
forNewDemo ? postData(form) : putData(form)
} else {
setErrors({ errs })
}
}
return (
<>
<form id={formId} onSubmit={handleSubmit}>
<textarea
name="projectVariable"
value={form.projectVariable}
onChange={handleChange}
/>
<input
name="projectName"
value={form.projectName}
onChange={handleChange}
/>
<button type="submit" className="btn">
Submit
</button>
</form>
<div>
{Object.keys(errors).map((err, index) => (
<li key={index}>{err}</li>
))}
</div>
</>
)
}
export default Form
the form is located at the /new page
pages/new.js
import Form from '../components/Form'
const New = () => {
const demoForm = {
projectVariable: [],
projectName: []
}
return <Form formId="add-demo-form" demoForm={demoForm} />
}
export default New
and here is my api at pages/api/demos/index.js
import dbConnect from '../../../lib/dbConnect';
import Demo from '../../../models/Demo';
import fs from 'fs';
export default async function handler(req, res) {
const {
query: { id },
method,
} = req
await dbConnect()
switch (method) {
case 'POST':
try {
const timestamp = (JSON.parse(JSON.stringify(new Date())));
const newJsonDirectory = "projects/";
const newJsonFile = newJsonDirectory + timestamp + ".json";
const activeUser = process.env.ACTIVE_USERNAME;
const newFileName = req.body.projectName;
fs.writeFileSync(newJsonFile, JSON.stringify(
{
"order": [
{
"username": activeUser,
"pages": [
{
"display": "projects",
"subpages": [
{
"route": timestamp,
"display": newFileName
}
]
}
]
}
]
}));
const demo = await Demo.create(
req.body
)
res.status(201).json({ success: true, data: demo })
} catch (error) {
res.status(400).json({ success: false })
}
break
default:
res.status(400).json({ success: false })
break
}
}
lets say I submit my values into the form input: projectName
input: hello-world
the projects folder would then contain a file called 2022-09-11T231607.396Z.json that looks like this
{
"order": [
{
"username": "projectmikey",
"pages": [
{
"display": "projects",
"subpages": [
{
"route": "2022-09-11T23:16:07.396Z",
"display": "hello-world"
}
]
}
]
}
]
}
The nested format of the JSON object is perfect however I can't seem to get the query id to show up in place of the date in the new file.
I have tried changing the timestamp variable to ${id} or id, among other things within my pages/api/demos/index.js but nothing seems to work.
the changed file
import dbConnect from '../../../lib/dbConnect';
import Demo from '../../../models/Demo';
import fs from 'fs';
export default async function handler(req, res) {
const {
query: { id },
method,
} = req
await dbConnect()
switch (method) {
case 'POST':
try {
const timestamp = (JSON.parse(JSON.stringify(new Date())));
const newJsonDirectory = "projects/";
const newJsonFile = newJsonDirectory + id + ".json";
const activeUser = process.env.ACTIVE_USERNAME;
const newFileName = req.body.projectName;
fs.writeFileSync(newJsonFile, JSON.stringify(
{
"order": [
{
"username": activeUser,
"pages": [
{
"display": "projects",
"subpages": [
{
"route": id,
"display": newFileName
}
]
}
]
}
]
}));
const demo = await Demo.create(
req.body
)
res.status(201).json({ success: true, data: demo })
} catch (error) {
res.status(400).json({ success: false })
}
break
default:
res.status(400).json({ success: false })
break
}
}
the file name shows up as undefined.json within the projects/ directory and the new contents looks are:
{
"order": [
{
"username": "projectmikey",
"pages": [
{
"display": "projects",
"subpages": [
{
"display": "hello-world"
}
]
}
]
}
]
}
I am relatively new to Node.js and have been banging my head around on this one
I am also wondering how to append to new JSON file rather that creating a new file upon each request...
If anyone could help me out with either of those 2 questions it would be greatly appreciated!
Thanks y'all
I am trying to do a helper function to check if login or not in react and nodejs... in nodejs everything is fine but I have problems at reactside to get value outside of function with return:
return output always = undefined ...
Here is the function;
note: console.log (response) = {status: 200, data: true, error:
false}
import axios from "axios";
import { useState,useEffect } from 'react';
export const AuthController = (event) => {
let data = {data : localStorage.getItem("imtakilogintoken")}
async function getData() {
try {
let res = await axios({
url: 'http://localhost:3005/profile',
method: 'post',
timeout: 8000,
data :data,
headers: {
'Content-Type': 'application/json',
}
})
return {
status : res.status,
data : res.data,
error : false
}
}
catch (err) {
return {
status : false,
data : false,
error : err
}
}
}
var output;
useEffect(() => {
getData().then(response => {
console.log (response)
output = response;
})
}, []);
return output;
}
For your kind information useEffect() is async function, the control first going to return statement and at this time return output (undefined) having undefined.
I think here no need to wrap your function into useEffect() instead just return from then only. so code would look like below.
import axios from "axios";
import { useState,useEffect } from 'react';
export const AuthController = (event) => {
let data = {data : localStorage.getItem("imtakilogintoken")}
async function getData() {
try {
let res = await axios({
url: 'http://localhost:3005/profile',
method: 'post',
timeout: 8000,
data :data,
headers: {
'Content-Type': 'application/json',
}
})
return {
status : res.status,
data : res.data,
error : false
}
}
catch (err) {
return {
status : false,
data : false,
error : err
}
}
}
//var output;
getData().then(response => {
console.log (response)
return response;
}).catch(err=>{
return err;
})
}
I think the problem itself is how you are trying to reach the data, on react I will recommend you to create a custom hook that you can implement on any component that loads later on the app, something like this:
import { useState, useCallback } from "react";
import axios from "axios";
const useAuthControl = () => {
const [hasResponse, setResponse] = useState(null);
const getData = useCallback(
async (data) => {
try {
let res = await axios({
url: 'http://localhost:3005/profile',
method: 'post',
timeout: 8000,
data :data,
headers: {
'Content-Type': 'application/json',
}
})
setResponse({
status : res.status,
data : res.data,
error : false
})
}
catch (err) {
setResponse({
status : false,
data : false,
error : err
})
}
},
[]
);
return [getData, {hasResponse}];
};
export default useAuthControl;
Sorry for such noob question but I'm a simply gave up. I've got below code which creates a POST request to the BE part of the app - user provides input (productCodes) and push submit button. That part works well, Vue sends request to BE but in the response FE should have received JSON with result: { id: 1234 }
How do I get this response and display it inside the app? I've got below:
imports.js
const createProductsRequest = (self, products) => {
const jwtToken = self.$store.state.idToken;
const payload = JSON.stringify({ product_codes: products['product_codes'].split(',') })
return axios
.post(`/api/v1/imports/products/`, payload,{
headers: {
Authorization: `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(response => response.data)
};
export {
createProductsRequest
};
sync_product.vue
<script>
import {
createProductsRequest
} from '../../api/imports'
import ModalController from '../general/modal_controller'
export default {
name: 'BackboneSyncProducts',
data() {
return {
styleCodes: [],
}
},
computed: {
productsToSyncAmount () {
return this.styleCodes.length
},
},
methods: {
async syncProducts() {
let confirmationText = `Do you want to ${this.productsToSyncAmount} sync products?`
if (this.productsToSyncAmount === 0) {
ModalController.showToast('', 'Type product codes for sync first, please!', 'warning')
}
else if (await ModalController.showConfirmation('Confirmation', confirmationText)) {
try {
ModalController.showLoader()
await createProductsRequest(this, this.styleCodes)
const successMessage = `${this.productsToSyncAmount} products have been queued for sync`
await ModalController.showToast('', successMessage)
} catch (data) {
const errorMessage = `Error occurred during queueing products to sync - `
ModalController.showToast('', errorMessage + data?.message, 'error')
} finally {
this.styleCodes = []
ModalController.hideLoader()
}
}
},
}
}
</script>
That's all what I have.
Instead of calling your request without getting the return value:
await createProductsRequest(this, this.styleCodes)
You can get the return value which is the result of the request :
const data = await createProductsRequest(this, this.styleCodes)
By doing that, data must contain the result of the request, as you mentionned { id: 1234 }.
--
If you want to use this result in your component, you can create a reactive value in data()
data() {
return {
styleCodes: [],
data: null
}
},
And store the result like this :
this.data = await createProductsRequest(this, this.styleCodes)
With that you can display it in your template for example :
<template>
<!-- There is a v-if because before doing the request, data is null -->
<div v-if="data">{{ data.id }}</div>
</template>
Data is returning undefine after they were fetched from the backend. When I console.log it, the data get returned in the console. But when I try to use the data, it was returning undefine
//Here is the my logic using axios but it didnot work, it keeps throwing 404 error and undefined data
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import axios from "axios";
const MyBaseQuery =
({ baseUrl } = { baseUrl: "http://localhost:5000" }) =>
async ({ url, method, data }) => {
try {
const result = await axios({ url: baseUrl + url, method, data });
return { data: result.data };
} catch (axiosError) {
let err = axiosError;
return {
error: { status: err.response?.status, data: err.response?.data },
};
}
};
export const getAllCarsApi = createApi({
reducerPath: "getAllCarsApi",
baseQuery: MyBaseQuery,
endpoints(build) {
return {
getAllCarsApi: build.query({
query: () => ({ url: "all-cars", method: "GET" }),
}),
};
},
});
export const { useGetAllCarsQuery } = getAllCarsApi;
I solve this by adding async and await in my functions.
I am trying to load items to my next.js page and it will fail:
import {getadminInfo} from '../../dataFetch/adminInfo'
import {addItem} from '../../dataFetch/catalog'
import {useState} from "react"
import { getList } from '../../dataFetch/catalogList'
export async function getStaticProps() {
const adminData = await getadminInfo()
const catlist = await getList()
return {
props: {
catlist,
adminData
}
}
}
export default function Main({allPostsData, adminData, catlist}) {
}
My function is :
export function getList() {
const pageInfo = {
page_size : "10",
page:"1"
}
const url = "http://localhost:8000/api/catalog/list?page_size="+pageInfo.page_size+"&page="+pageInfo.page;
try {
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
fData=JSON.parse(JSON.stringify(data.response))
console.log("Returned catalog")
return fData
})
.catch(error => console.log(error))
} catch (err) {
console.log(err)
}
}
The API works and I get the right info back but I cannot load it to the page:
Error: Error serializing .catlist returned from getStaticProps in "/admin/main".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
I found the issue. I did not implement the fetch correctly. It should have been async.
The reason I did not get the info is because nothing was returned.