inputbox onchange event is not updating state in React JS - reactjs

I'm learning React JS and trying to create a CRUD app. In a form, I could able to successfully fetch existing data and bind into forms controls. However, the onchange event of an input box does not seem to update the corresponding state. Sharing the code sample. Any input is highly appreciated.
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { useNavigate } from "react-router-dom";
function BookEdits() {
const navigate = useNavigate();
const [data, setData] = useState("");
const [title, setTitle] = useState("");
const params = useParams();
// Please ignore this part
const handleSubmit = (event) => {
event.preventDefault();
const requestOptions = {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: title,
description: description,
author: author,
}),
};
fetch(`https://localhost:7174/api/books/${params.id}`, requestOptions).then(
(response) => {
console.log(response);
if (!response.ok) alert("Error saving book details");
else alert("Book details is saved successfully");
navigate("/BooksList");
}
);
};
//
useEffect(() => {
fetch(`https://localhost:7174/api/Books/${params.id}`)
.then((response) => response.json())
.then(setData);
}, []);
// Does not change post onchange event
console.log(data.title);
return (
<form onSubmit={handleSubmit}>
<label>
Title:
<input type="submit" />
</form>
);
<input
type="text" value={data.title}
onChange={(e) => setTitle(e.target.value)}
/>
</label>
<input type="submit" />
</form>
);
}
export default BookEdits;
State should update post onchange event. What am I missing here? Thanks in advance.

data.title isn't the same as title. You call setTitle, which changes title, but you're logging data.title, which isn't the same attribute.

Related

Param values are undefined during axios file upload along with text upload using FormData

I'm building a simple books management application. In a particular page, I need to upload a book's details along with a picture of the book.
I'm using formData and axios to do this. In the same request, sending the optional image as well as the text inputs.
But on reading the text fields from the body in the server side, all of them are undefined.
How can I resolve this issue ?
addBooksForm.js
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import "./addbooksform.css";
import axios from "axios"
import { authContext } from "../../App";
const Addbooks = () => {
// eslint-disable-next-line
const [authDet, setAuthDet] = useContext(authContext);
const navigate = useNavigate()
const [values, setValues] = useState({
title: "",
author: "",
genreId: 1,
price: 0,
picture:null
});
const handleSubmit = async (e) => {
e.preventDefault();
let data = new FormData()
data.set("title", values.title)
data.set("author", values.author)
data.set("genreId", values.genreId)
data.set("price", values.price)
data.set("picture", values.picture)
console.log(values)
const response = await axios.post("http://localhost:5000/api/books/",data,
{
headers:{
Authorization:`Token ${authDet.accessToken}`
}
})
if (response.status === 200) {
navigate('/yourbooks');
} else {
console.log("Error occurred "+ response)
}
};
const onChange = (e) => {
setValues({ ...values, [e.target.name]: e.target.value });
};
const onFileChange = (e) => {
setValues({...values, [e.target.name] : e.target.files[0] })
}
return (
<div className="addbooks">
<form onSubmit={handleSubmit}>
<h3>Title</h3>
<input type="text" name="title" required={true} onChange={onChange} value={values.title}/>
<h3>Author</h3>
<input type="text" name="author" required={true} onChange={onChange} value={values.author}/>
<h3>Genre</h3>
<input type="number" name="genreId" required={true} onChange={onChange} value={values.genreId}/>
<h3>Price</h3>
<input type="number" name="price" required={true} onChange={onChange} value={values.price}/>
<h3>Upload picture</h3>
<input type="file" name="picture" onChange={onFileChange}/>
<button>Add</button>
</form>
</div>
);
};
export default Addbooks;
I have also tried adding content-type:multipart/form-data in the config
Server side controller:
const addBooks = (e) => {
const { title, author, price, genreId } = req.body;
// further processing
}
here, all the fields are undefined
server.js:
app.use(express.urlencoded({extended:true}))
app.use(express.json())
app.use(cors())
Any help is appreciated. thanks in advance !!

How to pass data from parent to child (react Modal)?

I have a page users.jsx (parent) and a component DialogEditUser.jsx (child) and i would like to pass a specific data of a user that is located in parent to child by it's id (using find method)
This passed data should be loaded to its input in react modal as a value.
users.jsx Code:
import React, { useState, useEffect } from 'react'
import DialogAddUser from 'src/components/DialogAddUser'
import { getUsers} from 'src/Service/api'
const Typography = () => {
const [users, setUsers] = useState([])
useEffect(() => {
getAllUsers()
}, [])
const deleteUserData = async (id) => {
setConfirmDialog({
...setConfirmDialog,
isOpen: false,
})
await deleteUser(id)
getAllUsers()
setNotify({
isOpen: true,
message: 'Article Deleted Successfully.',
type: 'error',
})
}
const getAllUsers = async () => {
let response = await getUsers()
setUsers(response.data)
console.log(response.data)
}
return ( //... )
DialogEditUsers.jsx Code:
import { useEffect, useState } from 'react'
import { getUsers, editUser } from '../Service/api'
const initialValue = {
id: '',
code: '',
article: '',
price: '',
vat: '',
status: '',
company_id: '',
}
export default function DialogAddUser() {
const [user, setUser] = useState(initialValue)
const { code, article, price, vat, status, company_id } = user
const normalize = (v) => ({
code: v.code,
article: v.article,
price: Number(v.price),
vat: Number(v.vat),
status: Number(v.status),
company_id: Number(v.company_id),
})
useEffect(() => {
loadUserDetails()
}, [])
const loadUserDetails = async () => {
const response = await getUsers(id)
console.log('loading user details ', response)
setUser(response.data.find((x) => x.id == id))
}
const editUserDetails = async () => {
const response = await editUser(id, normalize(user))
console.log('Edit user details ', response)
}
const onValueChange = (e) => {
console.log(e.target.value)
setUser({ ...user, [e.target.name]: e.target.value })
}
return (
<>
<CModal
visible={visible}
onClose={() => setVisible(false)}
backdrop={'static'}
keyboard={false}
portal={false}
>
<CModalHeader>
<CModalTitle>Edit Article:</CModalTitle>
</CModalHeader>
<CModalBody>
<CForm>
<CFormInput
type="text"
id="exampleFormControlInput1"
label="Code :"
placeholder="Enter Code"
text=" "
aria-describedby="exampleFormControlInputHelpInline"
onChange={(e) => onValueChange(e)}
value={code}
name="code"
/>
<CFormInput
type="text"
id="exampleFormControlInput2"
label="Article :"
placeholder="Enter Article"
text=" "
aria-describedby="exampleFormControlInputHelpInline"
onChange={(e) => onValueChange(e)}
value={article}
name="article"
/>
//...the rest of inputs...
api.js Code:
import axios from 'axios'
const baseURL = 'https://api.factarni.tn/article'
const token =
'eyJhbGciOiJSUzI1NiIsImtpZCI6IjIxZTZjMGM2YjRlMzA5NTI0N2MwNjgwMDAwZTFiNDMxODIzODZkNTAiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiZmFraHJpIGtyYWllbSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BSXRidm1uMS12dWJJcHNxTURKMkNTcDhVcTlmU3I1LUI1T3Y3RHY2SFRNMT1zMTMzNyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9mYWN0YXJuaSIsImF1ZCI6ImZhY3Rhcm5pIiwiYXV0aF90aW1lIjoxNjYzNzY3ODk5LCJ1c2VyX2lkIjoiaWhqM0JWM0hIRFhpVnUwdmpzV3ZidjMyRDdMMiIsInN1YiI6ImloajNCVjNISERYaVZ1MHZqc1d2YnYzMkQ3TDIiLCJpYXQiOjE2NjM3Njc4OTksImV4cCI6MTY2Mzc3MTQ5OSwiZW1haWwiOiJmYWtocmlpLmtyYWllbUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjEwODU1MTA3MjAwODIwNjMxMjI0NCJdLCJlbWFpbCI6WyJmYWtocmlpLmtyYWllbUBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJnb29nbGUuY29tIn19.bvRTxHfPtJrQjF2BjXqhs7ji738kma55LMFVRb8jkeraWP-JRBi-LRPa0d7OR_-BPwCGuRBXIb6980_PP8wjhBeDdB5B77GujiGn3nUvpPOFeIaM0L7muw1NKo4YCtS3v6ifuywypTbL3_5x3SBFZEH-QV0sp5DAzaA-P3Fn8AwP66o3cUPHGengGpZNsfkJ0FYcqzH-xpyKVVWV'
//i dont mind sharing this token, it's for you to test this code if you need.
const config = { headers: { Authorization: `Bearer ${token}` } }
export const getUsers = async (id) => {
id = id || ''
try {
return await axios.get(`${baseURL}`, config)
} catch (error) {
console.log('Error while calling getArticles api ', error)
}
}
export const editUser = async (id, user) => {
return await axios.put(`${baseURL}/${id}`, user, config)
}
The only node error i'm getting in terminal using this code above (because i dont know how to pass the proper id of specified user) is:
src\components\DialogEditUser.jsx
Line 45:37: 'id' is not defined no-undef
Line 47:47: 'id' is not defined no-undef
Line 51:37: 'id' is not defined no-undef
For better explanation the problem (i dont know how to use online snippets sorry):
So what i'm expecting is: When i click on Edit button, i should get a modal with form that are filled with user data (code, article, price, vat, status and company_id) in each input of the form as value, just like this gif below:
Also, console.log(response.data) in users page shows this:
few days back i also faced the same issue. Solution for me is to create state in parent component and pass state to child. Example for it-
Parent Class
const parent= ()=>{
const [name, setName]= useState('')
const [password, setPassword]= useState('')
return(
<Child setName={setName} setPassword={setPassword} />
)
}
Child Class
const Child = ({setPassword,setName})=>{
return(
<div>
<input type="text" placeholder="Enter Name" onChange={(e)=>setPassword(e.target.value)} />
<input type="text" placeholder="Enter Name" onChange={(e)=>setPassword(e.target.value)} />
</div>
)
}
Hope my answer will help you to solve your problem, if you still facing issue, lemme know i will help you.
In users.jsx, pass props of (user.id):
<DialogEditArticle props={user.id} />
Then, in DialogEditArticle.jsx, create a new data and call in it props:
const DialogEditArticle = (data) => {
console.log(data.props)
Now console.dev, you will get all the ids of user in database (because button edit is inside map function).
Result:

Sending Form Data From Frontend to Backend Using Axios Using React

I am trying to get input from the user in a form field and access the data in my backend server.js . I wanted to use this data in order to pass parameters to the Yelp Fusion API I am using. I know I can use axios for this but am unsure how to accomplish this at this time.
Here is my server.js:
const express = require('express')
const dotenv = require('dotenv').config()
const port = process.env.PORT || 5000
var axios = require('axios');
const app = express()
var cors = require('cors');
app.use(cors());
// app.get('/', (req, res) => {
// var config = {
// method: 'get',
// url: 'https://api.yelp.com/v3/businesses/search?location=Houston',
// headers: {
// 'Authorization': 'Bearer <API_KEY>
// }
// };
// axios(config)
// .then(function (response) {
// //const data = JSON.stringify(response.data);
// res.json(response.data)
// })
// .catch(function (error) {
// console.log(error);
// });
// })
app.listen(port, () => console.log(`Server started on port ${port}`))
Here is the App.js in which I need to pass the state from the input field to the backend:
import React,{useEffect,useState} from 'react';
import './App.css';
import axios from 'axios'
function App() {
const [zip,setZip] = useState("")
function handleSubmit() {
useEffect(() => {
axios.post("http://localhost:8000")
.then(res => {
console.log(res)
})
})
}
// const [results, setResults] = useState({})
// console.log(results)
// useEffect(() => {
// axios.get('http://localhost:8000').then(res => {
// console.log(res)
// })
// }, [])
return (
<div>
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
export default App;
Ok, here's what's wrong with your code:
hooks can only be placed on the first level of functional component (outside any sub-functions) and must be before any return statements.
Use effect will fire on render, in your code it looks like you wanna trigger it on event click.
I would do it this way if I were you:
function App() {
const [zip,setZip] = useState("");
const triggerAPI = useCallback(async () => {
// Use async await instead of chained promise
const res = await axios.post("http://localhost:8000", { zip: zip });
console.log(res)
}, [zip]);
const handleSubmit = useCallback((e) => {
e.preventDefault()
triggerAPI();
}, [triggerAPI])
const handleChange = useCallback((event) => {
setZip(event.target.value);
}, []);
return (
<div>
<form onSubmit={handleSubmit}>
<label>
ZIP:
<input type="text" value={zip} name="zip" onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
export default App;
changes:
I used useCallback to memoize the functions
I used async/await instead of chained promise (looks cleaner)
You use useEffect() function wrongly.
Handle the input element state properly.
To send data to the server you can use axios.post(url,data).then()
import React, { useState } from "react";
import axios from "axios";
function App() {
const [zip, setZip] = useState("");
function handleSubmit(event) {
event.preventDefault();
axios.post("http://localhost:8000", { zip: zip }).then((res) => {
console.log(res);
});
}
const handleChange = (event) => {
setZip(event.target.value);
};
return (
<div>
<form onSubmit={handleSubmit}>
<label>
ZIP:
<input type="text" value={zip} name="zip" onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
export default App;

Api marvel response don't show react

I try to training in react and want to make a form who call the api marvel when submitted with the current input and display the name + description of the character search.
The Api call is ok but when i submit the form nothing show any advice?
import React, { Component, useEffect, useState } from 'react'
import axios from 'axios'
const SearchEngine = React.forwardRef((props, ref) => {
const [asked, setAsked] = useState([]);
const [characterInfos, setCharacterInfos] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [loading, setLoading] = useState(true);
const [inputs, setInputs] = useState('');
const handleChange = (event) => {
setInputs(event.target.value);
console.log(inputs);
}
const getCharacters = (inputs) => {
setSearchTerm(inputs)
axios
.get(`https://gateway.marvel.com:443/v1/public/characters?name=${searchTerm}&apikey=XXX`)
.then(response => {
console.log(searchTerm)
console.log(response)
setCharacterInfos(response.data.data.results[0]);
setLoading(false);
console.log(response.data.data.results[0].name)
response.data.data.results.map((item) => {
return characterInfos.push(item.name)
})
localStorage.setItem(characterInfos, JSON.stringify(response.data))
if (!localStorage.getItem('marvelStorageDate')) {
localStorage.setItem('marvelStorageDate', Date.now());
}
})
.catch(error => {
console.log(error);
})
}
return (
<div className="search-container">
<h1>Character Infos</h1>
<form onSubmit={getCharacters}>
<input
type="text"
placeholder="Search"
value={inputs}
onChange={handleChange}
/>
<input type="submit" value="Envoyer" />
</form>
<ul>
<li>{characterInfos.name}</li>
</ul>
</div>
)
})
export default React.memo(SearchEngine)
Thanks for your help. Any to advice to show a list of all the character and make a search filter who work with minimum 3 characters?
getCharacters is fired with form submit event as param. You are assuming that is getting inputs from the state wrongly:
const getCharacters = event => {
event.preventDefault() // Prevent browser making undesired form native requests
// setSearchTerm(inputs); // Not sure what are you trying here but, again, inputs is a form submit event
axios
.get( // use searchValue as query string in the url
`https://gateway.marvel.com:443/v1/public/characters?name=${searchValue}&apikey=XXX`
)
.then(response => {
console.log(searchTerm);
console.log(response);
setCharacterInfos(response.data.data.results[0]);
setLoading(false);
console.log(response.data.data.results[0].name);
response.data.data.results.map(item => {
return characterInfos.push(item.name);
});
localStorage.setItem(characterInfos, JSON.stringify(response.data));
if (!localStorage.getItem("marvelStorageDate")) {
localStorage.setItem("marvelStorageDate", Date.now());
}
})
.catch(error => {
console.log(error);
});
};

Rendered more hooks than during the previous render. when posting form data with React Hooks

Ran into a problem with hooks today. I know there is a similar post, and I read the rules of using hooks. Right now when I post my form, it gives me that error. And I know that's because my hook is INSIDE an if statement. But how can I get it out? I don't know how else to use this hook if it's not in a function or a statement. Any advice would be greatly appreciated. Here is the code:
import React, { FunctionComponent, useState, useEffect } from 'react';
import usePost from '../hooks/usepost'
import Article from './article';
interface ArticlePosted {
title: string,
body: string,
author: string
}
const Post: FunctionComponent = () => {
const [details, detailsReady] = useState({})
const postArticle = (e) => {
e.preventDefault()
const postDetails = {
title: e.target.title.value,
body: e.target.body.value,
author: e.target.author.value
}
detailsReady(postDetails)
}
if (Object.keys(details).length !== 0) {
console.log(details)
usePost('http://localhost:4000/kb/add', details)
}
return (
<div>
<form onSubmit={postArticle}>
<p>
Title <input type='text' name='title' />
</p>
<p>
Body <textarea name='body' rows={4} />
</p>
<p>
Author <input type='text' name='author' />
</p>
<button type='submit'>Submit Article</button>
</form>
</div>
);
};
export default Post;
Custom Hook:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const usePost = (url, postDetails) => {
//set empty object as data
console.log(url, "DFLSKDJFSDLKFJDLKJFDLFJ")
console.log(postDetails)
useEffect(() => {
console.log('usePost running')
axios.post(url, postDetails)
.then(res => {
console.log(res)
return
})
}
, [postDetails]);
};
export default usePost
You can move the if-statement logic into the usePost hook.
const usePost = (url, postDetails) => {
useEffect(() => {
if (Object.keys(postDetails).length === 0){
return console.log('Not posting'); // Don't post anything if no details
}
// Otherwise, post away
console.log('usePost running')
axios.post(url, postDetails)
.then(res => {
console.log(res)
return
})
}
, [postDetails]);
};

Resources