I'm making a post admin tool with jsonplaceholder api and I'm having a problem with the put method. How can I update the text input modifications? It's not working even with the fixed data.
const EditPost = ({ match }) => {
const [state, setState] = useState({
title: "",
body: "",
});
const [error] = useState("");
const { id } = match.params;
const [post, setPost] = useState(null);
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
setPost(response.data);
});
}, []);
const updatePost = () => {
axios
.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
title: "Hello World!",
body: "Post updated."
})
.then((response) => {
setPost(response.data);
});
}
<form noValidate autoComplete="off" onSubmit={updatePost}>
<TextField
id="outlined-basic"
label="Title"
defaultValue={post.title || ""}
name="title"
type="text"
onChange="" />
<TextField
id="outlined-multiline-flexible"
label="Body"
defaultValue={post.body || ""}
name="body"
type="text"
onChange="" />
<Button
type="submit">
Update Post
</Button>
I would suggest you restructure your component as such:
const EditPost = ({ match }) => {
const [error] = useState("");
const { id } = match.params;
const [postTitle, setPostTitle] = useState("");
const [postBody, setPostBody] = useState("");
useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
setPost(response.data);
});
}, []);
const updatePost = () => {
axios
.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
title: postTitle,
body: postBody,
})
.then(({ data }) => {
setPostTitle(data.title);
setPostBody(data.body);
});
};
return (
<form noValidate autoComplete="off" onSubmit={updatePost}>
<TextField
id="outlined-basic"
label="Title"
value={postTitle}
name="title"
type="text"
onChange={(e) => setPostTitle(e.target.value)}
/>
<TextField
id="outlined-multiline-flexible"
label="Body"
value={postBody}
name="body"
type="text"
onChange={(e) => setPostBody(e.target.value)}
/>
<Button type="submit">Update Post</Button>
</form>
);
};
To be able to update the state as the user types, you need to control your form fields. The way to do this is to supply each field with a value prop, and use the onChange prop to update the state of the fields. This is the recommended by the mui team.
Furthermore, we have moved our state from a single object (which is reminiscent of the days of class components), to separate state objects (postBody and postTitle) which should reduce the number of times parts of the component have to re-render.
Related
I am learning React from few days and I am trying to learn Axios, Everything worked fine until I tried to insert data, which I successfully inserted but My React Page did not updated contact list immediately.
HERE's MY CODE:
App.js
import Axios from "axios";
import React, { useEffect, useState } from "react";
import Add__Contact from "./api/Add__Contact";
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const url = "http://localhost:3006/contacts";
//get all availbale contacts
useEffect(() => {
// get all contacts async
async function getUsers() {
Axios.get(url).then((response) => {
setContacts(response.data);
});
}
getUsers();
console.log(contacts);
// get all contacts non-async
// Axios.get(url).then((response) => {
// setContacts(response.data);
// });
}, []);
//add new contact to server
const addContact = () => {
const saveRes = Add__Contact({ name, phone });
};
// view
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
export default App;
Add__Contact.js
import Axios from "axios";
const Add__Contact = async ({ name, phone }) => {
Axios({
method: "post",
url: "http://localhost:3006/contacts",
headers: {
"Content-Type": "application/json",
},
data: {
name,
phone,
},
}).then(function (res) {
// console.log(res);
});
};
export default Add__Contact;
db.json
{
"contacts": [
{
"name": "Max",
"phone": "123456",
"id": 1
},
{
"name": "John",
"phone": "13454",
"id": 2
},
{
"name": "Candy",
"phone": "1245781245",
"id": 3
}
]
}
I am not sure why it's not updating list automatically, I thought useEffect will run everytime I click and call Add__Contact(). Can you please tell me what did i missed or doing wrong?
I am not sure if useEffect hook is good for what I want to achieve or not, so please guide me. Thank you in advance.
data insertion is working fine, but after I insert it, it's not updating ui, even if I am fetching data inside useEffect
Your useEffect hook is only ran once - when the component mounts. This is because you have given it an empty dependency array (the 2nd argument).
The dependency array determines when the effect function will run. If its empty, it will only run when the component is mounted (displayed for the very first time). If you add something in the array, the effect will run on mount, and whenever the provided value changes.
In your case, you have an event (the click event from the Add to Contacts button) after which you want your data to be fetched again. But you also want to fetch data when the page loads.
One way to do it is something like this:
const Add__Contact = async ({ name, phone }) => {
// Return the Promise returned from the Axios call
return Axios({
method: "post",
url: "http://localhost:3006/contacts",
headers: {
"Content-Type": "application/json",
},
data: {
name,
phone,
},
});
};
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const url = "http://localhost:3006/contacts";
// Add a function to fetch contacts
const fetchContacts = async () => {
const res = await Axios.get(url);
setContacts(res.data);
};
// Effect that fetches contacts when the component loads
useEffect(() => {
fetchContacts();
}, []);
//add new contact to server
const addContact = async () => {
// await the Promise returned
const saveRes = await Add__Contact({ name, phone });
// Fetch the contacts list again
await fetchContacts();
};
// view
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
So you'r Contacts Array is not updated .Even you got a data from axios call .
Like if axios is returning data then i think you'r state in not updating then you have to use
setContacts((prv)=>[...prv,...res.data])
If you'r facing problem on Add time . Then make a separate function then use that in useEffect() && your ADD_Contact() .
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const getContacts = async () => {
const res = await Axios.get('http://localhost:3006/contacts');
setContacts(res.data);
};
useEffect(() => {
getContacts();
}, []);
const addContact = async () => {
const saveRes = await Add__Contact({ name, phone });
await getContacts();
};
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
I want to create a simple application where you can search for images. The application is written in React using fetch (I can't use axios) and Unsplash API. My current attempt renders a list of images with a static value "cars" into the link as shown: https://api.unsplash.com/search/photos?query=**cars**
In the code example below I am using a variable "${query}" to be able to search for images but it toes not work. I need help to figure out how to fix that. Thanks in advance!
code:
import React from "react";
import { useState, useEffect } from "react";
export default function App() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [query, setQuery] = useState("");
useEffect(() => {
fetch(`https://api.unsplash.com/search/photos?query=${query}`, {
headers: {
Authorization: "Client-ID UnsplashId",
},
})
.then((response) => {
if (!response.ok) {
throw new Error(
`This is an HTTP error: The status is ${response.status}`
);
}
return response.json();
})
.then((actualData) => {
console.log(actualData);
setData(actualData.results);
setError(null);
})
.catch((err) => {
setError(err.message);
setData(null);
});
}, []);
return (
<div>
{/* onSubmit={this.handleSubmit} */}
<form>
<label>
<input
placeholder="Search"
type="text"
// value={this.state.value}
// value="cars"
onChange={(e) => setQuery(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
{data &&
data.map(({ id, description, urls }) => (
<img key={id} alt={description} src={urls.regular} />
))}
</div>
);
}
I think you want to achieve conditionally firing an effect
Example
useEffect(() => {
// This will execute whenever 'query' variable changes.
}, [ query ]);
// You can bind the state using the 'value' attribute.
<input
placeholder="Search"
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
I did not quietly get the question but I think you want to do the search every time the input is changed, hence I recommend using an input instead of the form and adding "query" to the "useEffect" conditions:
useEffect(() => {
fetch(`https://api.unsplash.com/search/photos?query=${query}`, {
headers: {
Authorization: "Client-ID UnsplashId",
},
})
.then((response) => {
// here
}, [query]);
<input
placeholder="Search"
type="text"
onChange={(e) => setQuery(e.target.value)} />
Just asking how to get this and put it to my API. Here's a link: https://web.5writer.com/user/signup
{
"countryCallingCode": "374",
"nationalNumber": "23131223",
"number": "+37423131223",
"country": "AM"
}
This is the body of my API
{
dial_code,
mobile,
iso_code
}
This is my code
export default function Home() {
const toast = useToast()
const router = useRouter();
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [dial_code, setDial] = useState('');
const [mobile, setMobile] = useState('');
const [iso_code, setIso] = useState('');
async function handleSubmit (e) {
e.preventDefault();
setLoading(true);
fetch(`https://web.5writer.com/user/signup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dial_code,
mobile,
iso_code,
}),
})
.then((res) =>
res.json().then((body) => ({
status: res.status,
body,
}))
)
.then((resp) => {
console.log(resp);
setLoading(false);
if (resp.body.status === true) {
setDial('');
setMobile('');
setIso('');
toast({
title: 'Success!',
description: resp.body.message,
status: 'success',
duration: 6000,
isClosable: true,
position: 'top',
variant: 'left-accent',
});
router.push('../AllOrders/dashboard');
}
else {
toast({
title: 'ERROR!',
description: resp.body.message,
status: 'error',
duration: 6000,
isClosable: true,
position: 'top',
variant: 'left-accent',
});
}
})
}
return (
<div>
{success && <Notification />}
<main>
<Container >
<Box
w='17.8em'
p={0}
borderRadius='5px'
mt={3}
mb={-4}
mx='auto'
pos='relative'
marginLeft='-1em'
>
{loading && (
<Progress
pos='absolute'
top='0'
left='0'
width='100%'
isIndeterminate
borderTopLeftRadius='6px'
borderTopRighRtadius='6px'
size='sm'
colorScheme='blue'
/>
)}
<form onSubmit={handleSubmit}>
<FormControl className="">
<PhoneNumber
placeholder="enter phone number"
value={dial_code}
onChange={(e) => setDial(e.target.value)}
/>
</FormControl>
<div className="form-group2 d-md-flex">
<div className="w-50 text-left">
<input type="checkbox" className="checkL"/>
<div className="remember">
I have read the <a className="terms">Terms and Condition</a>
</div>
</div>
</div>
<Button
type='submit'
mt='0'
size='sm'
colorScheme='#2CBEFF'
disabled={loading}
pos='relative'
className="lbutton"
>
Register
{/* {loading && <Spinner pos='absolute' color='red.500' />} */}
</Button>
<div className="form-group3">
<p className="text-center">Already have an account?
<Link href="/Login"><a data-toggle="tab" className="Log">Log In</a></Link></p>
</div>
</form>
</Box>
</Container>
</main>
</div>
);
}
This code is working but the problem is I only got one data using onChange. Is it possible to use 3 onChange? or is there any method to get 3 data in just one input.
Give me a piece of advice thank you.
It's still not entirely clear what your issue is, but based on the comments it seems you want a single state variable and change handler to manage 3 inputs. You generally accomplish this by associating a name attribute with each input. The name attribute is accessed via the onChange event and can update the specific nested state.
Example:
const initialState = {
country: "",
countryCallingCode: "",
number: ""
};
function App() {
const [{ country, countryCallingCode, number }, setState] = React.useState(
initialState
);
const changeHandler = (e) => {
const { name, value } = e.target; // <-- destructure from event
setState((state) => ({
...state,
[name]: value // <-- use name as dynamic key
}));
};
const submitHandler = (e) => {
e.preventDefault();
const data = {
dial_code: countryCallingCode,
number,
country
};
setState(initialState);
console.log(data);
};
return (
<div className="App">
<form onSubmit={submitHandler}>
<div>
<label>
Country Code
<input
type="text"
value={countryCallingCode}
name="countryCallingCode"
onChange={changeHandler}
/>
</label>
</div>
<div>
<label>
Number
<input
type="text"
value={number}
name="number"
onChange={changeHandler}
/>
</label>
</div>
<div>
<label>
Country
<input
type="text"
value={country}
name="country"
onChange={changeHandler}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
Update
If you want to use the PhoneInput component you need to map its onChange handler to your own since it passes directly the input value to the handler.
const changeHandler = (e) => {
const { name, value } = e.target;
setState((state) => ({
...state,
[name]: value
}));
};
Here the changeHandler is expecting an onChange event object. You can pass any object you like, and so long as it has the correct shape and properties the handler can handle it.
<PhoneInput
value={number}
name="number"
onChange={(value) =>
changeHandler({
target: {
name: "number",
value
}
})
}
/>
Update 2
Ok, I think I understand what you're after now. You want just a single phone number input and then to parse the country code, country, and phone number from the single state.
Check parsePhoneNumber
There's no need for any custom onChange handlers, just update state with the PhoneInput value and when you are ready, parse the state.
const [state, setState] = React.useState(null);
const submitHandler = (e) => {
e.preventDefault();
const {
countryCallingCode: dial_code,
country: iso_code,
number: mobile
} = parsePhoneNumber(state);
const data = {
dial_code,
mobile,
iso_code
};
setState(null);
// do with data now what you need
};
...
<PhoneInput value={state} onChange={setState} />
hi that's a little dirty but you can create your state somethings like this
let [values,setValues] = useState({phoneNumber : '', dial:'', code:''});
let [inputState, setInputState] = useState('phoneNumber');
const onInputChange = (e) => {
const { target : { value } } = e;
setValues(preventValues => ({...preventValues, inputState : value}))
}
const handleSubmit = (inputStateName) => {
// do your functionality then
setInputState(inputStateName);
}
Within my handleSubmit method if I hardcode the message and publish the method works as intended. However if I replace "hello stomp" with the input state or submit with any input at all I get Uncaught TypeError: Cannot read property 'publish' of undefined" any insight here will be greatly appreciated
export const Comms = () => {
const [messages, setMessages] = useState();
const [input, setInput] = useState("");
const client = new Client();
client.configure({
brokerURL: "ws://localhost:2019/socket",
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
onConnect: function () {
client.subscribe("/topic/messages", function (msg) {
console.log("WS-MESSAGE: ", msg.body);
});
},
});
const handleSubmit = (event) => {
client.publish({ destination: "/topic/messages", body: "Hello stomp" });
event.preventDefault();
};
useEffect(() => {
client.activate();
}, []);
return (
<div className="comms-cont">
<h1 className="comms-header">Messaging</h1>
<form onSubmit={handleSubmit} className="form-1">
<input
className="forminput"
type="text"
name="message"
onChange={(e) => {
setInput(e.target.value);
}}
/>
</form>
</div>
);
};
You want to use a controlled input component. Add the prop value to the input like this:
<input
className="forminput"
type="text"
name="message"
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
/>
What am I trying to do : Trying to enter the first name and last name of a person and display all the entered name below using redux global state management.
When I try to create a new object by entering data into the input fields, the 1st item is being displayed empty both on the view page and console log and from the 2nd item, all the items are being displayed correctly. I encountered this error previously but couldn't fix it.
Actual page:
import {useDispatch} from 'react-redux';
import {useState} from 'react';
const Inc = ()=>{
const[data, setData] = useState({});
const[firstName, setFirstName] = useState('');
const[lastName, setLastName] = useState('');
const dispatch = useDispatch();
const newData={
first_name: "",
last_name : ""
}
const handleFieldFirst=async (e)=>{
await setFirstName(e.target.value);
}
const handleFieldLast= async (e)=>{
await setLastName(e.target.value);
}
const handler = async (e)=>{
e.preventDefault();
newData.first_name = firstName ;
newData.last_name = lastName ;
await setData(newData);
console.log(data);
dispatch({type: 'INC', payload : data});
document.getElementById("form1").reset();
}
return(
<form id="form1" type ="submit" name="login" >
<input onChange={e => handleFieldFirst(e)} name="first" type="text" placeholder="First name"></input>
<input onChange={e => handleFieldLast(e)} name="last" type="text" placeholder="Last name"></input>
<button onClick={e => handler(e)}>Submit</button>
</form>
);
}
export default Inc;
View page
import {useSelector} from 'react-redux';
const Disp = ()=>{
const count = useSelector(state => state.counterReducer);
return(
<div>
{count.map((d) => <li key={d.last_name}>{d.first_name} {d.last_name}</li>)}
</div>
);
}
export default Disp;
enter image description here
First of all, you have to change your code. you have to use async and await only if the function returns a promise
import { useState } from "react";
import { useDispatch } from "react-redux";
const Inc = () => {
const [data, setData] = useState({
firstName: "",
lastName: "",
});
const dispatch = useDispatch();
const handler = (e) => {
e.preventDefault();
dispatch({ type: "INC", payload: data });
setData({
firstName: "",
lastName: "",
});
};
return (
<form id="form1" type="submit" name="login">
<input
onChange={(e) =>
setData({
...data,
firstName: e.target.value,
})
}
name="first"
type="text"
placeholder="First name"
value={data.firstName}
/>
<input
onChange={(e) =>
setData({
...data,
lastName: e.target.value,
})
}
name="last"
type="text"
placeholder="Last name"
value={data.lastName}
/>
<button onClick={handler}>Submit</button>
</form>
);
};
export default Inc;
by default useSelector will return the default state (you didn't attach reducer code so I'm assuming that the reducer returns default state) so add a condition to render <li>....</li> only the length of the array is greater than 0.