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

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

Related

React form not rendering with selectedRow data

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.

How to bind an input's value to a link in react

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

React - how to use map to pass an array of options on input of type unform select

I have a select input where I want to pass an options array object using map but when rendering my page only one option even when the array I'm using in the map has several items it insists on presenting only one
below all the code:
export default function PesquisarAulas() {
const dispatch = useDispatch();
const user = useSelector((state) => state.user.profile);
const [docente, setDocente] = useState([]);
const [docenteDisciplinas, setDocenteDisciplinas] = useState([]);
const [disciplinas, setDisciplinas] = useState([]);
const updateDisciplinas = [...disciplinas];
async function carregarDocente() {
const response = await api.get(`docente/findByUser/${user.id}`);
return response.data;
}
async function carregarDisciplinasDocente() {
const response = await api.get(`docente/${docente.id}/disciplinas`);
return response.data;
}
async function carregarDisciplina(disc) {
const response = await api.get(`disciplinas/${disc.id_disciplina}`);
return response.data;
}
useEffect(() => {
carregarDocente().then((value) => {
setDocente(value);
});
}, [user]);
useEffect(() => {
carregarDisciplinasDocente().then((value) => {
setDocenteDisciplinas(value);
});
}, [docente]);
useEffect(() => {
docenteDisciplinas.map((docDisc) =>
carregarDisciplina(docDisc).then((value) => {
updateDisciplinas.push(value);
setDisciplinas(updateDisciplinas);
})
);
}, [docenteDisciplinas]);
console.log(disciplinas);
function handleSubmit() {}
return (
<Container>
<div className="title">Pesquisar aulas</div>
<Form onSubmit={handleSubmit}>
<div className="input-box">
<span>Ano Letivo:</span>
<Input
name="anoLetivo1"
type="text"
placeholder="Introduza o ano letivo"
/>
</div>
<div className="input-box">
<span>Disciplinas:</span>
<Select
name="tech"
options={disciplinas.map((disciplina) => ({
id: disciplina.id,
title: disciplina.nome,
}))}
placeholder="Nenhum selecionado"
/>
</div>
<div className="input-box">
<span>Aulas de:</span>
<Input name="dataInicio1" type="datetime-local" id="pickup_time" />
<span style={{ marginLeft: '10px' }}>ate:</span>
<Input name="dataFinal1" type="datetime-local" id="pickup_time" />
</div>
<div className="input-box">
<span>Curso:</span>
<Input name="curso1" type="text" placeholder="Introduza o curso" />
</div>
<div className="input-box">
<span>Unidade Curricular:</span>
<Input
name="unidadeCurricular1"
type="text"
placeholder="Introduza a unidade curricular"
/>
</div>
<hr />
<button type="submit">Pesquisar</button>
</Form>
</Container>
);
}
the focus of the problem is on these two code snippets here:
const [disciplinas, setDisciplinas] = useState([]);
const updateDisciplinas = [...disciplinas];
useEffect(() => {
docenteDisciplinas.map((docDisc) =>
carregarDisciplina(docDisc).then((value) => {
updateDisciplinas.push(value);
setDisciplinas(updateDisciplinas);
})
);
}, [docenteDisciplinas]);
<Select
name="tech"
options={disciplinas.map((disciplina) => ({
id: disciplina.id,
title: disciplina.nome,
}))}
placeholder="Nenhum selecionado"
/>
I think the problem is that when the select is rendered only one item is inserted in the disciplines array,
I think maybe if there was a way to make Select wait until all the items in the disciplines array are ready so it can render, the problem would be solved.
Try using the select like this:
<select placeholder="Nenhum selecionado" >
{{disciplinas.map((disciplina) => {
<option value={disciplina.nome}>
{disciplina.nome}
</option>
})}}
</select>

Pass multiple variable with POST method in React app

I'm trying to post multiple variables to my Postgres database using a form with React.
This is my current script:
const InputAddress = () => {
const [name, setName] = useState("");
const [problem, setProblem] = useState("");
const [date, setDate] = useState("");
const onSubmitForm = async (e) => {
e.preventDefault();
try {
const body = {
name,
problem,
date,
};
console.log(params);
const response = await fetch("http://localhost:5000/logements", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
console.log(response);
window.location = "/";
} catch (error) {
console.log(error.message);
}
};
return (
<Fragment>
<h1 className="text-center mt-5">Add your address</h1>
<form className="d-flex mt-5" onSubmit={onSubmitForm}>
<label>Name</label>
<input
type="text"
className="form-control"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<label>Comment</label>
<input
type="text"
className="form-control"
value={problem}
onChange={(e) => setProblem(e.target.value)}
/>
<label>Date</label>
<input
type="text"
className="form-control"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<button className="btn btn-success">Submit</button>
</form>
</Fragment>
);
};
My problem seems to be with the fetch method.
When I submit the form, I get this error message in the console :
bind message supplies 1 parameters, but prepared statement "" requires 3
Is there a simple fix to this problem?

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