How can i sent limited fields while updating form in react - javascript

user.js
import React, { useState, useEffect } from 'react';
const Users = () => {
const [user, setUser] = useState({
name: '',
email: '',
phoneNumber: '',
role: '',
status: ''
}); ======>>>> I want to sent above fields only however while fetching user it fetchs extra fields that I don't want to send while handle submit
const { name, email, phoneNumber, role, status } = user;
const handleChange = e => {
setUser({ ...user, [e.target.name]: e.target.value });
};
const loadUser = async () => {
const res = await Axios.get(`http://localhost:3001/user/fetch/${id}`)
setUser(res.data.response.info)
};
useEffect(() => {
loadUser();
}, []);
const handleSubmit = async e => {
e.preventDefault();
await Axios.patch(`http://localhost:3001/user/${id}`, user)
}; ====>>> I want to sent limited user field here
return (
<form onSubmit={e => handleSubmit(e)} >
<div className="form-group">
<label htmlFor='name'>Name:</label>
<input type="text" name="name" id="name" className="form-control" value={name || ''} onChange={e => handleChange(e)} />
</div>
)
}
I am updating user it is working fine but what i get error it is fetching extra field in user state how can i handle limited fields to sent in handle submit while submitting form like I don't want extra fields to sent to backend. I want only fields which is in state

Why don't you set the fields what you want to set and ignore the rest.
const loadUser = async () => {
const res = await Axios.get(`http://localhost:3001/user/fetch/${id}`);
const _user = res.data.response.info;
setUser({
email: _user.email,
//// rest,
})
};
Or else just allowKeys which you want before patching
function allowKeys(obj, keysAllowed) {
const newObj = {}
for (const key in obj) {
if (keysAllowed.includes[key]) {
newObj[key] = obj[key]
}
}
return newObj
}
// at the time of patching
const toBePatchedUser = allowKeys(user, ["email"... /** only allowed keys here */])

Related

How to use react/tanstack query useMutation in my component

I'm currently converting the logic in my mern (with typescript) project to use React/Tanstack query to learn this tool better.
I want to use useMutation to handle the post request logic from the details inputted in the form, in this login component but can't figure out how to do this. Any tips would be appreciated thanks. Below is the code from my login component
const Login = () => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errorMsg, setErrorMsg] = useState("");
const [state, setState] = useContext(UserContext);
const handleSubmit = async (e: { preventDefault: () => void }) => {
e.preventDefault();
let response;
const { data: loginData } = await axios.post("http://localhost:5001/auth/login", {
email,
password,
});
response = loginData;
if (response.errors.length) {
return setErrorMsg(response.errors[0].msg);
}
setState({
data: {
id: response.data.user.id,
email: response.data.user.email,
stripeCustomerId: response.data.user.stripeCustomerId,
},
loading: false,
error: null,
});
localStorage.setItem("token", response.data.token);
axios.defaults.headers.common["authorization"] = `Bearer ${response.data.token}`;
navigate("/dashboard");
};
return (
<div className="login-card">
<div>
<h3>Login</h3>
</div>
<form onSubmit={handleSubmit}>
<div className="login-card-mb">
<label>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div className="login-card-mb">
<label>Password</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</div>
{errorMsg && <p>{errorMsg}</p>}
<button type="submit">Submit</button>
</form>
</div>
);
};
After setting up your project to use React Query ( Check the docs if you have not). You want to extract your api call to a separate function that takes an object. This object will hold the values you would like to post.
const Login = (dataToPost) => {
let res = await axios.post('url', dataToPost)
return res.data
}
Now that you have that, you can import useMutation from React Query. Once imported you can now use the hook. UseQuery, useMutation both contain a data variable so no need to create state for the data returned from your endpoint. In this example, I'm deconstructing the data and loading state. But most importantly the mutate function. Which allows you to fire off your api call. We add our api call to the hook. I'm renaming the mutate function to doLogin. It's a habit
const {data,isLoading,mutate:doLogin} = useMutation(Login)
Finally we can just call mutate(objectWithValues) wherever you want in your code. The data will initially be null and isLoading will be true once called. To tie it all together. Your handleSubmit could look as follows
const handleSubmit = () => {
e.preventDefault();
doLogin({email,password})
}
You also have the option of running functions on a success or error of the mutation
const {data,isLoading,mutate: doLogin} =
useMutation(Login, {
onError: (err) => console.log("The error",err),
onSuccess:(someStuff)=>console.log("The data being returned",someStuff)
})

how to pass only those values which changed in POST request body

I have multiple state variables, that contains data entered in a form by the user. Since this form is only meant to update the existing values, I have to pass in only those values that have changed from its initial value (the one returned from the GET request).
State:
const [name, setName] = useState(props.user?.name ?? null);
const [lang, setLang] = useState(props.user?.lang ?? null);
const [enableChecks, setEnableChecks] = useState(props.user?.checkEnabled ?? false)
In the event that the user only changed the name, how can I pass in only name in the request body?
What I have tried: I have the user props, so I have multiple if statements that check if the props matches the state. If it doesn't, then I add it to the request payload. This works, but when there's a lot of state, there will be a lot of if statements, which isn't nice to look at.
Is there a better way to do this?
Instead of having multiple state variables, you can have a single state variable like
const [state, setState] = useState(props.user)
and then change handler should look like
const handleChange = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};
finally, when submitting the form you can make your body data for post request like
const handleSubmit = () => {
const requestData = {}
for (const [key, value] of Object.entries(state)){
if(props.user[key] !== value) {
requestData[key] = value
}
}
axios.post('some api url', responseData)
}
You can keep your state in an object, and then only update field state when the updatedUser and user state values are different.
//use `import` in your real component instead
//import { useState } from 'react';
const { useState } = React;
//fake your user prop
const userProp = {
name: "Name",
lang: "English",
}
function App(props) {
const [user, setUser] = useState(props.user);
const [updatedUser, setUpdatedUser] = useState({});
const handleChange = (e) => {
const newlyUpdatedUser = {
...updatedUser,
}
if(props.user[e.target.name] === e.target.value) {
delete newlyUpdatedUser[e.target.name]
} else {
newlyUpdatedUser[e.target.name] = e.target.value
}
setUpdatedUser(newlyUpdatedUser);
setUser({
...user,
[e.target.name]: e.target.value
})
};
console.log(updatedUser)
return (
<React.Fragment>
<label>
Name:
<input value={user.name} name="name" onChange={handleChange} />
</label>
<label>
Lang:
<input value={user.lang} name="lang" onChange={handleChange} />
</label>
</React.Fragment>
);
}
ReactDOM.render(<App user={userProp} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Nothing displays when I want to edit in ReactJS

When I want to edit my input nothing displays and I have this error:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components input form
But I have output in my console.log.
import axios from "axios";
import React, { useEffect } from "react";
import { useParams } from 'react-router-dom'
import { useState } from "react";
const Edituser = () => {
const { id } = useParams();
const [user, setUser] = useState({
name: "",
email: "",
password: "",
c_password: "",
role: ""
});
const { name, email, password, c_password, role } = user;
const onInputChange = e => {
setUser({ ...user, [e.target.name]: e.target.value })
};
useEffect(() => {
Updaate();
},
[]
);
const Updaate = async () => {
const response = await axios.get(`http://127.0.0.1:8000/api/edit-user/${id}`);
setUser(response.data)
console.log(response.data)
};
const Updateuser = async e => {
e.preventDefault();
await axios.put(
`http://127.0.0.1:8000/api/update-user/${id}`, user
);
};
return (
<form className="register-form">
<div className="head-register" >
<h3>register</h3>
</div>
<label htmlFor="name">Username</label>
<input name="name" onChange={e => onInputChange(e)} value={name} ></input>
<h1>hello</h1>
</form>
);
}
export default Edituser;
Nothing displays when I want to edit in React.

React JS: A component is changing a controlled input to be uncontrolled

My code
UserEdit.jsx
import useForm from "../../utils/useForm";
import LoadingBtn from "../../utils/loadingButton";
import { getUser } from "../../store/users/userActions";
const UserEdit = () => {
//declare form data and scmena
const formInput = {
name: "",
};
const schema = {
name: Joi.string().required().min(3).max(191).label("Name"),
};
//dispatch on first mount
const dispatch = useDispatch();
const params = useParams();
useEffect(() => {
setErrors({});
dispatch(getUser(params.id));
}, []);
const {
handleChange,
handleSubmit,
formData,
setFormData,
errors,
setErrors,
} = useForm(formInput, doSubmit, schema);
function doSubmit() {
console.log("handle Submit", formData);
}
const isSubmitting = useSelector((state) => state.users.isSubmitting);
const user = useSelector((state) => state.users.user);
useEffect(() => {
console.log("useEffect user >>", user);
setFormData({
...formData,
name: user.name,
});
}, [user]);
return (
<input
className={`form-control ${
errors["name"]
? "is-invalid"
: ""
}`}
type="text"
id="name"
name="name"
required=""
placeholder="Enter your name"
onChange={handleChange}
value={formData.name}
/>
);
};
export default UserEdit;
UseForm.jsx
const useForm = (formInput, callback, schema = {}) => {
const [formData, setFormData] = useState(formInput);
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (event) => {
const { name, value } = event.target;
setFormData({
...formData,
[name]: value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
//handle error
setErrors(validate(formData));
setIsSubmitting(true);
};
const validate = (formData) => {
const { error } = Joi.validate(formData, schema, {
abortEarly: false,
});
if (!error) return {};
const validataionErrors = {};
for (let item of error.details) {
validataionErrors[item.path[0]] = item.message;
}
return validataionErrors;
};
useEffect(() => {
//check if there are any errors
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
setIsSubmitting(true);
}
}, [errors]);
return {
handleChange,
handleSubmit,
formData,
setFormData,
errors,
setErrors,
};
};
export default useForm;
I googled about the error and it mentioned that state need to be initialized at first with the field. However, I have already defined initial state as
const formInput = {
name: "",
};
I could not find how could I fix this, I am open to restructure the useForm hooks if that is the one which causing trouble.
currently, if I uncomment the following line on userEdit.jsx, the warning will be gone, but also the edit form becomes empty as well
setFormData({
...formData,
name: user.name,
});

Draft-js do not save database

I am unable to save description as part of the component's state. I can only save the title. How do I save title and description to the database?
const BlogCreate = ({ history }) => {
const [blogCreate, setBlogCreate] = useState({
title: "",
description: ""
});
const [editorState, setEditorState] = useState(
EditorState.createEmpty(),
);
const handleChange = ({ currentTarget }) => {
const { name, value } = currentTarget;
setBlogCreate({...blogCreate, [name]: value});
};
const onEditorStateChange = editorState => {
setEditorState(editorState);
};
const handleSubmit = async event => {
event.preventDefault();
const data = draftToHtml(convertToRaw(editorState.getCurrentContent()));
try {
await blogAPI.create(blogCreate, data);
} catch (error) {
console.log(error)
}
}
console.log(data);
}
return(
<Field type="text" name="title" error={errors.title} value={blogCreate.title}
onChange={handleChange}
/>
<Editor name="description" editorState={editorState} onEditorStateChange={editorState => onEditorStateChange(editorState)}
/>
<button type="submit">Save</button>
);
}
export default BlogCreate;
Based on the full code you've provided me, I realised that you aren't properly updating the blogCreate state whenever there is a change at the Editor component.
The onEditorStateChange() should be updating the blogCreate state, and in addition, changeValue() needs to return the result value.
const changeValue = editorState => {
const value = ....
return value;
};
const onEditorStateChange = editorState => {
const description = changeValue(editorState);
setBlogCreate({
...blogCreate,
description,
});
setEditorState(editorState);
};
This way, description will be properly updated on your state, and it will be sent to your server side when you make the blogAPI.create() request.

Categories