i am not able to render api response to form, i am using same form for add and for edit too. i want when i click on Add button it should show initial empty value , but when i click edit button , it should so particular user data , but i am not able to achieve it.
I have tried
import React, { useState, useContext, useEffect } from "react";
import { useParams, useLocation } from "react-router-dom";
import { globalC } from "./context";
export default function DashboardForm() {
const { detail, apiCalling } = useContext(globalC);
useEffect(() => {
apiCalling(id);
}, []);
const { pathname } = useLocation();
const action = pathname.split("/")[1];
const [name, setName] = useState(action === "add" ? "" : detail.name);
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const { id } = useParams();
return (
<div style={{ height: "100vh" }}>
<form>
<input
name="name"
type="text"
placeholder="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
name="username"
type="text"
placeholder="username"
onChange={(e) => setUsername(e.target.value)}
/>
<br />
<input
name="email"
type="text"
placeholder="email"
onChange={(e) => setEmail(e.target.value)}
/>
</form>
</div>
);
}
i am use class based context api , user get api is called in context,
Codesandbox link : link
login and go to dashboard to try the example
Mind that your useState for name will be first called when the API call hasn't succeeded yet, giving you an empty name. Then once you do get the proper name in detail, the state is already created.
One solution would be something like:
if (!name && detail.name) {
setImmediate(() => setName(detail.name));
}
So once the API call gave you the name, and while the name input field is still empty, update it with the received name.
Another solution is to only show the form once the API request succeeded, and show a loading screen in the meantime.
Related
I'm creating a simple react application with 3 user roles. On the login page localhost:3000/login I have fields for username and password. Once those details are entered and the login button is clicked, the data is sent to the backend (running node js) and that data is used to query a MySql database. If the entered data matches a user in the database, the userId, name, password, and role is sent to the backend. This data is then sent to the front end. I can read the retrieved data from the front end and up to this point it works fine. However, when I'm trying to redirect a user according to the role, say the role is doctor and I want to redirect the user to localhost:3000/doctor , it goes to localhost:3000/doctor momentarily and switches to localhost:3000/login?. Shown below is the code for the login component.
import { useState } from "react";
import Axios from 'axios';
import { useNavigate } from 'react-router-dom'
import './login.css';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
let navigate = useNavigate()
const handleLogin = () => {
Axios.post("http://localhost:3001/login",
{
email: email,
password: password,
},
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => {
console.log('response 1', response.data[0]['role'])
if (response.data[0]['role'] === 'doctor') {
navigate('/doctor');
}
});
};
return (
<div>
<form>
<h3>Electronic Prescription System</h3>
<h3>Login</h3>
<label>Email Address</label>
<input
className="inputs"
type="text"
placeholder="email"
onChange={(e) => {
setEmail(e.target.value)
}}
/>
<label>Password</label>
<input
className="inputs"
type="password"
placeholder="password"
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Log in</button>
</form>
</div>
)
};
export default Login;
If I remove all the code inside the handleLogin function and just have navigate('/doctor'); it redirects properly.
The routes are inside the Main component as shown below.
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Login from "./pages/Login/Login";
import Doctor from "./pages/Doctor/Doctor";
import Patient from "./pages/Patient/Patient";
import Pharmacy from "./pages/Pharmacy/Pharmacy";
const Main = () => {
return (
<Routes>
<Route path="login" element={<Login />} />
<Route path="doctor" element={<Doctor />} />
<Route path="patient" element={<Patient />} />
<Route path="pharmacy" element={<Pharmacy />} />
</Routes>
);
}
export default Main;
The Doctor Component:
import { HeaderPanel } from '../../components/headerPanel/headerPanel'
import { PrescribePanel } from '../../components/prescribePanel/prescribePanel'
import { PrescriptionsList } from '../../components/prescriptionsList/prescriptionsList'
import './styles.css';
export const Doctor = () => {
return (
<>
<HeaderPanel />
<div className='wrapper'>
<PrescribePanel />
<PrescriptionsList />
</div>
</>
);
}
export default Doctor
I'm using react-router-dom version 6.6.1 and the react version is 18.2.0.
Tried using a useEffect hook to capture the role changing and redirecting, but id did not work either.
What I suspect is happening here is that the "log in" button is submitting the form which takes the default form action and reloads the page, the current route path being "/login". button elements have a type="submit" attribute value by default.
To resolve I'd suggest attaching handleLogin to the form element's onSubmit event handler and calling preventDefault on the onSubmit event object to prevent submitting the form and prevent reloading the page. This should allow the authentication logic to complete as expected.
Try to get yourself in the habit of being specific with the button element's type attribute.
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const handleLogin = (e) => { // <-- onSubmit event object
e.preventDefault(); // <-- don't take form action
Axios.post(
"http://localhost:3001/login",
{ email, password },
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => {
if (response.data[0]['role'] === 'doctor') {
navigate('/doctor');
}
});
};
return (
<div>
<form onSubmit={handleLogin}> // <-- form onSubmit event handler
<h3>Electronic Prescription System</h3>
<h3>Login</h3>
<label>
Email Address
<input
className="inputs"
type="text"
placeholder="email"
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
Password
<input
className="inputs"
type="password"
placeholder="password"
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type="submit"> // <-- be specific with button type
Log in
</button>
</form>
</div>
);
};
export default Login;
First.js
import { useState } from "react";
import { Link } from "react-router-dom";
const First = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
}
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
></input>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
></input><br/>
<Link to={'/second'} state={{ state: { name : name , phone : phone } }}><button>submit</button></Link>
</form>
</div>
);
}
export default First;
I try to send Object data using Link/state to another component.
Second.js
import {useLocation} from 'react-router-dom';
const Second = () => {
const location = useLocation();
console.log(location.state);
console.log(location.state.name);
return (
<div className="Second">
<h1>This is Second Input Page</h1>
<form>
<dd>data 3</dd>
<input></input>
<dd>data 4</dd>
<input></input><br/>
<button>submit</button>
</form>
</div>
);
}
export default Second;
However, while I can access to (location.state), I can not access to (location.state.name). Why is that?
Output
state: {name: 'Myname', phone: 'myphone'}
[[Prototype]]: Object
--------------------
undefined
The output shows that the code line "console.log(location.state);" works, but to the "console.log(location.state.name);", it shows undefined.
It's because you passed an object with state as the root property, i.e.
state={{ state: { name: name, phone: phone } }}
so to access it in the receiving route it is location.state.state.name.
You really don't need to nest the data you want to pass under a state property when using the Link component, it's not quite the same as when using the navigate function.
<Link to="/second" state={{ name, phone }}>
<button>submit</button>
</Link>
It may also be considered semantically incorrect HTML to nest a button element within an anchor tag (via Link) element. Use the useNavigate hook and issue an imperative navigation action from the form's submit handler. In this case the navigate function's second argument, the options object, *does* expect the state to be passed under the state` property.
Example:
import { Link, useNavigate } from "react-router-dom";
const First = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
navigate("/second", { state: { name, phone } });
};
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
/>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
/>
<br/>
<button>submit</button>
</form>
</div>
);
};
export default First;
I am trying to post new information about a cow to my cow API, however, everytime i hit the submit button on my frontend, it seems to be sending an empty object rather than the name of the cow, description of the cow, and image of the cow (via url). What is causing it to send an empty object versus my desired data?
Here is the frontend code:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';
const baseUrl = "http://localhost:3001/api/cows"
function Display({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {
axios.get(baseUrl)
.then(res => res.data)
.then(res => {
setNameOfCow(res.name)
setImageOfCow(res.image)
setDescriptionOfCow(res.description)
})
return (
<div>
<p>{nameOfCow}</p>
<img src={imageOfCow}/><p>{descriptionOfCow}</p>
</div>
)
}
function Input({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {
function handleSubmit(e) {
e.preventDefault()
let newObject = {
name: nameOfCow,
description: descriptionOfCow,
image: imageOfCow
}
axios.post(baseUrl, newObject)
}
return (
<div>
<form>
<label htmlFor="name">name: </label>
<input type="text" id="name" onChange={(e) => {
const eTarget = e.target.value
setNameOfCow(eTarget)}}/><br></br>
<label htmlFor="description">description: </label>
<input type="text" id="description" onChange={(e) => {
const eTargetDesc = e.target.value
setDescriptionOfCow(eTargetDesc)}}/><br></br>
<label htmlFor="image">image url: </label>
<input type='text' id="image" onChange={(e) => {
const eTargetImage = e.target.value
setImageOfCow(eTargetImage)}}/><br></br>
<button type="submit" onSubmit={handleSubmit}>Add a cow!</button>
</form>
</div>
)
}
function App() {
const [nameOfCow, setNameOfCow] = useState('')
const [descriptionOfCow, setDescriptionOfCow] = useState('')
const [imageOfCow, setImageOfCow] = useState('')
return (
<div className="App">
<Input imageOfCow={imageOfCow} setNameOfCow={setNameOfCow} setDescriptionOfCow={setDescriptionOfCow} setImageOfCow={setImageOfCow} />
<Display setNameOfCow={setNameOfCow} setImageOfCow={setImageOfCow} setDescriptionOfCow={setDescriptionOfCow} nameOfCow={nameOfCow} imageOfCow={imageOfCow} descriptionOfCow={descriptionOfCow} />
</div>
);
}
export default App
and here is the image showing the empty objects being posted:
Looking into your Input component props:
function Input({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {...
We can see that you missing to pass this props when using this component:
<Input imageOfCow={imageOfCow} setNameOfCow={setNameOfCow} setDescriptionOfCow={setDescriptionOfCow} setImageOfCow={setImageOfCow} />
The correct way to use is something like:
<Input
imageOfCow={imageOfCow}
nameOfCow={nameOfCow}
descriptionOfCow={descriptionOfCow}
setNameOfCow={setNameOfCow}
setDescriptionOfCow={setDescriptionOfCow}
setImageOfCow={setImageOfCow}
/>
Also the correct way to prevent the form default behavior is setting the onSubmit and the handleSubmit at the form attribute (you can remove from the button):
<form onSubmit={handleSubmit}>
Otherwise a very nice change is to put your axios request inside a useEffect hook to prevent your app from making request every time it re-render.
Using something like this the app will make the request only at the first component render.
const getCow = async (baseUrl) => {
const cow = await axios.get(baseUrl);
setNameOfCow(cow.name);
setImageOfCow(cow.image);
setDescriptionOfCow(cow.description);
};
useEffect(() => {
getCow(baseUrl);
}, []);
I currently have a form that is auto generated based on the amount of "Activities" a current user has. Each activity has a name and a value. My goal is to submit these values to the backend to update them. Yet I can't figure out how to reference these inputs. I was trying to read about using "ref"s, yet they come back with {current:null}
Here is the auto generated list (excuse the placeholder text)
When I inspect console here is what I find from the ref:
Here is my code:
import React, { useEffect } from "react";
import { useDispatch, useStore } from "react-redux";
import { connect } from "react-redux";
import * as actions from "../store/actions/patientSide";
export function ActivityTemplates() {
const dispatch = useDispatch();
const store = useStore();
const ref = React.createRef();
useEffect(() => {
// Update the document title using the browser API
dispatch(actions.getTodaysActivityTemplates());
}, []);
const activities = store.getState().patientSide.todays_activities;
const listedStuff = activities.map((activity) => (
<div>
{activity.activity_name}
<label for="value"> Value: </label>
<input
type="number"
id="value"
defaultValue={activity.value}
min="0"
max="10"
></input>
</div>
));
const saveActivities = () => {
var inputs = ref;
console.log(inputs);
// Insert code to run the call to the backend
};
return (
<div>
<h1> Activity Templates</h1>
<form id="form" onSubmit={saveActivities()} ref={ref}>
{listedStuff}
<input type="submit" name="save" />
</form>
</div>
);
}
export default ActivityTemplates;
I am very new to React and JS and honestly have very little idea of what I'm doing, but if someone could point me in the right direction that would be awesome!
EDIT: After sleeping on it, I've realized I've just been trying to force react into my HTML. I believe I should instead use a React Form Hook, and do it properly from the ground up.
<form onSubmit={handleOnSubmit}>
<label>User Name</label>
<input type="text" name="username" /><br/>
<label>Password</label>
<input type="password" name="password" /><br/>
<input type="submit" value="Submit" />
</form>
const handleOnSubmit = (event) => {
const formData = new FormData(event.target);
const formDetails = {};
event.preventDefault();
for (let entry of formData.entries()) {
formDetails[entry[0]] = entry[1];
};
console.log("formDetails", formDetails);
}
You are getting the input fields value from "FormData" on onSubmit.
const saveActivities = (event) => {
event.preventDefault();
const data = new FormData(event.target);
// Insert code to run the call to the backend
}
You need to store the value
const [value, setValue] = React.useState();
Then give your input a onChange={e => setValue(e.target.value)}
I would change the id though
Is there any way of inject text of database into the input to be editable? not like placeholder.
Here is a picture as example.
In this case the text is placed as placeholder and obviously can not be editable without erase all.
Here is the code:
<div style={modalStyle} className={classes.paper}>
<form className="update-note create-note">
<input
className="input-edit-note"
name="title"
onChange={(e) => setTitle(e.currentTarget.value)}
value={title}
placeholder={props.title}
/>
<textarea
name="content"
onChange={(e) => setContent(e.currentTarget.value)}
value={content}
placeholder={props.content}
/>
<Fab onClick={submitNote}>
<AddCircleIcon />
</Fab>
</form>
</div>
Whatever you put in value={...} will be visible to edit in the textfield.
If it's props that you want to merge with component local data then I'd suggest doing so via useState and useEffect
Either this:
const Example = (props) => {
const [title, setTitle] = useState(props.title);
...
}
or like this
const Example = (props) => {
const [title, setTitle] = useState('');
useEffect(() => {
setTitle(props.title);
}, [props.title]);
...
}
And then use the value in the input tag
<input
...
value={title}
...
/>
Example component:
// When initiating this component, make sure to pass "title" as props
const Example = (props) => {
// Title is bound to state within this component
const [title, setTitle] = useState(props.title);
const _onChangeTitle = e => {
setTitle(e.target.value);
}
return (
<input
className="input-edit-note"
name="title"
onChange={_onChangeTitle}
value={title} // title will always be state title
placeholder={props.title} // props.title will always remain the same
/>
)
}
once you get your data from you db save it in your component state as i see you're already doing:
const MyComponent = (props) => {
const [title, setTitle] = useState(props.title);
...
}
then set theinput's value equal to the title data you recieve from your db.
At the onChange trigger the setTitle function that will update your title state.
<input
className="YourProfileInput"
placeholder="Insert Title"
value={title}
onChange={(e) => setTitle(e.currentTarget.value)}
/>