React form not rendering with selectedRow data - reactjs

I am new to React.js so forgive me if this is an obvious mistake. I have a list of data with an update button on each row and it is supposed to open a form with the relevant fields already populated with the selectedRow.data but the controls are blank. The URL in Postman is giving the right response so that's not the problem. Any help would be appreciated.
Also getting this error in my console:
"Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component"
Here is my code:
import React, { useState, useContext, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { AppContext } from '../context/AppContext';
import AdminPortal from '../apis/AdminPortal'
const OrganizationUpdateForm = (props) => {
const {id} = useParams()
const { organizations } = useContext(AppContext);
const [name, setName] = useState("");
const [shortName, setShortName] = useState("");
const [parentOrg, setParentOrg] = useState("");
const [website, setWebsite] = useState("");
const [comments, setComments] = useState("");
useEffect(() => {
async function fetchData () {
const response = await AdminPortal.get(`organizations/${id}`);
console.log(response.data.data);
setName(response.data.data.organizations.name);
setShortName(response.data.data.organizations.short_name);
setParentOrg(response.data.data.organizations.parent_org);
setWebsite(response.data.data.organizations.website);
setComments(response.data.data.organizations.comments);
};
fetchData();
}, []);
let Navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
const updateOrganization = await AdminPortal.put(`organizations/${id}`, {
name,
short_name: shortName,
parent_org: parentOrg,
website,
comments
});
Navigate("/");
};
return (
<div>
<form action=''>
<div className="form-group">
<label htmlFor='name'>Name</label>
<input
value={name}
onChange={(e) => setName(e.target.value)}
id="name"
className="form-control"
type="text"
/>
</div>
<div className="form-group">
<label htmlFor='short_name'>Short Name</label>
<input
value={shortName}
onChange={(e) => setShortName(e.target.value)}
id="name"
className="form-control"
type="text"
/>
</div>
<div className="form-group">
<label htmlFor='parent_org'>Parent Organization</label>
<input
value={parentOrg}
onChange={(e) => setParentOrg(e.target.value)}
id="name"
className="form-control"
type="text"
/>
</div>
<div className="form-group">
<label htmlFor='website'>Website</label>
<input
value={website}
onChange={(e) => setWebsite(e.target.value)}
id="name"
className="form-control"
type="text"
/>
</div>
<div className="form-group">
<label htmlFor='comments'>Comments</label>
<input
value={comments}
onChange={(e) => setComments(e.target.value)}
id="name"
className="form-control"
type="text"
/>
</div>
{/* <button
type="submit"
onClick={handleSubmit}
className="btn btn-primary"
>
Submit
</button> */}
</form>
</div>
)
}
export default OrganizationUpdateForm
{
"status": "success",
"results": 1,
"data": {
"organizations": [
{
"id": 20,
"name": "International Aids Vaccine Initiative",
"name_localized": "IAVI",
"parent_org_id": null,
"website": "",
"comment": "",
"create_date": "2019-08-26T12:42:49.457Z",
"logo": null,
"short_name": "IAVI"
}
]
}
}
Above is the response when I hit the API with "http://localhost:3000/api/v1/organizations/20" which is the correct response. Below is the code for AdminPortal
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:3000/api/v1/"
})
This is a screen capture of the network response
The last line in the {id} has no response and I'm wondering if that might be a clue and I'm also surprise to see the port change from 3001 to 3000. I did change to response.data.organizations which seems to match with the structure in the response but that didn't make a difference. Here is what I changed:
useEffect(() => {
async function fetchData () {
// const response = await AdminPortal.get(`organizations/${id}`);
const response = await AdminPortal.get(`organizations/${id}`);
console.log(response.dataorganizations);
setName(response.data.organizations.name);
setShortName(response.data.organizations.short_name);
setParentOrg(response.data.organizations.parent_org);
setWebsite(response.data.organizations.website);
setComments(response.data.organizations.comments);
};
fetchData();
}, []);
Thanks for your support.

Related

React UI/dom is not updating after data insertion in json-server

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>
);
};

reactjs "TypeError: Cannot read properties of undefined (reading 'params')"

I'am new using reactjs and looks like I am following the old tutorial with old version of react router. So the objectives is when I want to edit the user detail, the form must be filled with data of previous user before update. I've already comunicate with the backend using axios and it worked fine, but the problem is in "id = props.match.params.id". and here is my code:
UserEdit.tsx
import axios from "axios";
import React, { SyntheticEvent, useEffect, useState } from "react";
import { Navigate } from "react-router-dom";
import Wrapper from "../../components/Wrapper";
import { Role } from "../../models/role";
const UserEdit = (props: any) => {
const [first_name, setFirstName] = useState('');
const [last_name, setLastName] = useState('');
const [email, setEmail] = useState('');
const [role_id, setRoleId] = useState('');
const [roles, setRoles] = useState([]);
const [redirect, setRedirect] = useState(false);
let id: number;
useEffect(() => {
(
async () => {
const response = await axios.get('roles');
setRoles(response.data);
id = props.match.params.id;
const {data} = await axios.get(`users/${id}`);
setFirstName(data.first_name);
setLastName(data.last_name);
setEmail(data.email);
setRoleId(data.role_id);
}
)()
}, []);
const submit = async (e: SyntheticEvent) => {
e.preventDefault();
await axios.put('users', {
first_name,
last_name,
email,
role_id
});
setRedirect(true)
}
if(redirect) {
return <Navigate to="/users"/>
}
return (
<Wrapper>
<form onSubmit={submit}>
<h1 className="h3 mb-3 fw-normal">Edit user</h1>
<div className="form-floating">
<input className="form-control" placeholder="First Name" defaultValue={first_name} onChange={e => setFirstName(e.target.value)} required/>
<label htmlFor="floatingInput">First Name</label>
</div>
<div className="form-floating">
<input className="form-control" placeholder="Last Name" defaultValue={last_name} onChange={e => setLastName(e.target.value)} required/>
<label htmlFor="floatingInput">Last Name</label>
</div>
<div className="form-floating">
<input type="email" className="form-control" placeholder="Email Address" defaultValue={email} onChange={e => setEmail(e.target.value)} required/>
<label htmlFor="floatingInput">Email Address</label>
</div>
<div className="form-floating">
<select className="form-control" id="floatingRole" placeholder="Role" value={role_id} onChange={e => setRoleId(e.target.value)} required>
{roles.map((r: Role) => {
return (
<option key={r.id} value={r.id}>{r.name}</option>
)
})}
</select>
<label htmlFor="floatingRole">Role</label>
</div>
<button className="w-100 btn btn-lg btn-primary" type="submit">Save</button>
</form>
</Wrapper>
);
};
export default UserEdit;
As you can see in this image below, the data isn't show up and get the error message like this
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'params')
I really appreciate if anyone wants to help me solve this problem so I can learn further. Thankyou
In react-router-dom#6 there are no longer "route props", i.e. no location, history (well, now navigate), and no match.
Use the useParams hook to access the route path parameters.
...
import { Navigate, useParams } from "react-router-dom";
...
const UserEdit = (props: any) => {
const { id } = useParams();
...
useEffect(() => {
(async () => {
const response = await axios.get('roles');
setRoles(response.data);
const { data } = await axios.get(`users/${id}`);
setFirstName(data.first_name);
setLastName(data.last_name);
setEmail(data.email);
setRoleId(data.role_id);
})();
}, []); // <-- add `id` to dependency array if you need to be responsive
...
return (
...
);
};
It seems like your params not pass correctly .
Maybe you need to get URL parameters by using useParams
like document (https://v5.reactrouter.com/web/example/url-params)
, or check the Router component path format is correct in your React Router version .
In React Router ^v5.3.0 :
<Route path="users/:id/edit" />

Rendering content in react js on submit button

I'm fetching content from external api using axios and react hooks.
Currently, I'm rendering {renderedResults} without any button. But I want to add submit button and then only render the results when someone clicks on it.
How to implement it in this scenario?
I tried official doc..but no success...
import React, { useEffect, useState } from "react";
import axios from "axios";
import "./Search.css";
const Search = () => {
const [vpincode, setvPincode] = useState("");
const [vdate, setvDate] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
const search = async () => {
const { data } = await axios.get("https://api", {
params: {
pincode: vpincode,
date: vdate,
},
});
setResults(data.sessions);
};
search();
}, [vpincode, vdate]);
const renderedResults = results.map((result) => {
return (
<div>
{result.name}
{result.address}
</div>
);
});
return (
<div className="container">
<div className="w-25 mb-3">
<label className="form-label ">Enter Pincode:</label>
<input
value={vpincode}
type="text"
className="form-control"
placeholder="Pincode"
onChange={(e) => setvPincode(e.target.value)}
></input>
</div>
<div className="w-25 mb-3">
<label className="form-label">Date:</label>
<input
value={vdate}
type="text"
className="form-control"
placeholder="Date"
onChange={(e) => setvDate(e.target.value)}
></input>
</div>
{renderedResults}
</div>
);
};
export default Search;
Code not tested, but you can do something like this...
import React, { useEffect, useState } from "react";
import axios from "axios";
import "./Search.css";
const Search = () => {
const [vpincode, setvPincode] = useState("");
const [vdate, setvDate] = useState("");
const [results, setResults] = useState([]);
const fetchApiContent = async (_) => {
const { data } = await axios.get("https://api", {
params: {
pincode: vpincode,
date: vdate,
},
});
setResults(data.sessions);
}
const renderedResults = results.map((result) => {
return (
<div>
{result.name}
{result.address}
</div>
);
});
return (
<div className="container">
<div className="w-25 mb-3">
<label className="form-label ">Enter Pincode:</label>
<input
value={vpincode}
type="text"
className="form-control"
placeholder="Pincode"
onChange={(e) => setvPincode(e.target.value)}
></input>
</div>
<div className="w-25 mb-3">
<label className="form-label">Date:</label>
<input
value={vdate}
type="text"
className="form-control"
placeholder="Date"
onChange={(e) => setvDate(e.target.value)}
></input>
</div>
{renderedResults}
<button onClick={fetchApiContent}>Fetch API Content</button>
</div>
);
};
export default Search;

Data Fetching with React using useEffect

What I am trying to do is when the user click the edit button, this will send him to a new page where he can modify the info he already entered. The problem I am facing is that the new page is not showing the data previously entered, so that the user can make his changes. Also, the submit button to send those changes is not working. These are the errors I am getting: src\components\RestaurantList.jsx
Line 25:8: React Hook useEffect has a missing dependency: 'setRestaurants'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Line 31:19: 'response' is assigned a value but never used no-unused-vars
src\components\UpdateRestaurant.jsx
Line 9:12: 'restaurants' is assigned a value but never used no-unused-vars
Line 38:8: React Hook useEffect has a missing dependency: 'code'. Either include it or remove the dependency array react-hooks/exhaustive-deps
My code for the component I am working on:
import React, {useState, useContext, useEffect} from 'react';
import { useHistory, useParams } from 'react-router-dom';
import RestaurantFinder from '../apis/RestaurantFinder';
import { RestaurantsContext } from '../context/RestaurantsContext';
const UpdateRestaurant = (props) => {
const {code} = useParams();
const {restaurants} = useContext(RestaurantsContext);
let history = useHistory();
const [name, setName] = useState("");
const [value, setValue] = useState ("");
const [strain, setStrain] = useState ("");
const [weight, setWeight] = useState ("");
const [authors, setAuthors] = useState ("");
const [number, setNumber] = useState ("");
const [page, setPage] = useState ("");
const [date, setDate] = useState ("");
useEffect(() => {
const fetchData = async () => {
const response = await RestaurantFinder.get(`/${code}`);
console.log(response.data.data);
setName(response.data.data.restaurant.name);
setValue(response.data.data.restaurant.value);
setStrain(response.data.data.restaurant.strain);
setWeight(response.data.data.restaurant.weight);
setAuthors(response.data.data.restaurant.authors);
setNumber(response.data.data.restaurant.number);
setPage(response.data.data.restaurant.page);
setDate(response.data.data.restaurant.date);
};
fetchData();
}, []);
const handleSubmit = async(e) => {
e.preventDefault();
const updatedRestaurant = await RestaurantFinder.put(`/${code}`, {
name,
value,
strain,
weight,
authors,
number,
page,
date,
});
console.log(updatedRestaurant);
history.push("/");
};
return (
<div>
<form action="">
<div className="form-group">
<label htmlFor="name">Name</label>
<input value={name} onChange={(e) => setName(e.target.value)} code="name" className="form-control" type="text" />
</div>
<div className="form-group">
<label htmlFor="Value">Value</label>
<input value={value} onChange={(e) => setValue(e.target.value)} code="value" className="form-control" type="float" />
</div>
<div className="form-group">
<label htmlFor="Strain">Strain</label>
<input value={strain} onChange={(e) => setStrain(e.target.value)} code="strain" className="form-control" type="text" />
</div>
<div className="form-group">
<label htmlFor="Weight">Weight</label>
<input value={weight} onChange={(e) => setWeight(e.target.value)} code="weight" className="form-control" type="float" />
</div>
<div className="form-group">
<label htmlFor="Author">Author</label>
<input value={authors} onChange={(e) => setAuthors(e.target.value)} code="authors" className="form-control" type="text" />
</div>
<div className="form-group">
<label htmlFor="Number">Number</label>
<input value={number} onChange={(e) => setNumber(e.target.value)} code="number" className="form-control" type="number" />
</div>
<div className="form-group">
<label htmlFor="Page">Page</label>
<input value={page} onChange={(e) => setPage(e.target.value)} code="page" className="form-control" type="number" />
</div>
<div className="form-group">
<label htmlFor="date">Date</label>
<input value={date} onChange={(e) => setDate(e.target.value)} code="date" className="form-control" type="number" />
</div>
<button onClick={handleSubmit} type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
)
}
export default UpdateRestaurant
for reusable code, it may be best to just do something like this.
This is probably not the answer, but I hope it helps you find out the answer.
const [data, setData ] = useState({restraunt.loaded:"false"});
useEffect(() => {
const fetch = async () => {
const response = await RestaurantFinder.get(`/${code}`);
console.log(response.data.data);
setData({...response.data.data, restraunt.loaded:"true"});
};
fetch();
},[Data.restraunt.loaded])
const {name, value , page, loaded } = Data.restaurant;
return (
<div><h1>{loaded}</h1>
</div>
)
If it shows loaded as false then you know it is because of the data not loading.

Trying to bind submit and save to localStorage

Here my App.js code, I am trying to bind and capture the "handlesubmit" function, and then append to an empty list which will be populated. Thanks.
import React from 'react';
const App = () => {
const [songs, setSongs] = React.useState([]);
React.useEffect(() => {
const data = localStorage.getItem('songs');
if (!data) { }
setSongs(JSON.parse(data));
}, []);
React.useEffect(() => {
localStorage.setItem('songs', JSON.stringify(songs));
});
const handleSubmit = data => {
setSongs([data]);
}
return (
<main>
<h1>Music Editor</h1>
<form onSubmit={this.props.handleSubmit(this.handleSubmit.bind(this))} autoComplete="false">
<label for="title">Title:</label>
<input type="text" id="title" name="title" placeholder="Type title/name of song" value="" />
<input type="submit" value="Add song" />
</form>
</main>
);
}
export default App;
The explanation is commented in the code itself.
Here is the codesandbox link to see the App working.
import React from 'react';
const App = () => {
const [songs, setSongs] = React.useState([]);
// use another state for song title
const [songTitle, setSongTitle] = React.useState('');
React.useEffect(() => {
const data = localStorage.getItem('songs');
// only update the state when the data persists
if (data) setSongs(JSON.parse(data));
}, []);
// update the localStorage whenever the songs array changes
React.useEffect(() => {
localStorage.setItem('songs', JSON.stringify(songs));
}, [songs]);
// inside the functional component, there is no "this" keyword
const handleSubmit = (event) => {
event.preventDefault();
// append the new song title with the old one
setSongs([
...songs,
songTitle
]);
}
return (
<main>
<h1>Music Editor</h1>
<form onSubmit={handleSubmit} autoComplete="false">
<label htmlFor="title">Title:</label>
<input
type="text"
id="title"
name="title"
placeholder="Type title/name of song"
value={songTitle}
onChange={e => setSongTitle(e.target.value)}
/>
<input type="submit" value="Add song" />
</form>
</main>
);
}
export default App;

Resources