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.
Related
I've written resetpasswordHandler form with react-form-hook package and passing the data object to authSlice along with resetToken (string) as parameter.
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router-dom";
import { reset, resetPassword } from "../features/auth/authSlice";
const ResetPassword = () => {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm({
defaultValues: {
password: "",
confirm_password: ""
},
mode: "onTouched",
});
const dispatch = useDispatch()
const {resetToken} = useParams()
const {isSuccess} = useSelector(state => state.auth)
const navigate = useNavigate()
const resetPwdHandler = async (data) => {
console.log(data) //{password: '123', confirm_password: '123'}
await dispatch(resetPassword({data, resetToken}))
await dispatch(reset())
};
useEffect(() => {
if(isSuccess){
navigate('/login', {replace: true})
}
}, [isSuccess, dispatch, navigate])
return (
<div>
<form onSubmit={handleSubmit(resetPwdHandler)}>
<label className="input-lable">Enter Password</label>
<input
className="input"
type="password"
placeholder="Password"
{...register("password", {
required: "Password is required",
})}
/>
<p>{errors.password?.message}</p>
<label className="input-lable">Confirm Password</label>
<input
className="input"
type="password"
placeholder="Confirm Password"
{...register("confirm_password", {
required: true,
validate: (val) => {
if (watch('password') !== val) {
console.log("hi")
return 'Confirm Password must be same as password'
}
}
})}
/>
<p>{errors.confirm_password?.message}</p>
<button>Reset Password</button>
</form>
</div>
);
};
export default ResetPassword;
Here is reset password code in authSlice
export const resetPassword = createAsyncThunk("auth/resetPassword", async ({passwordData, resetToken}, {rejectWithValue}) => {
console.log(passwordData, resetToken) //getting undefined for passwordData
try {
return await resetUserPassword(passwordData, resetToken)
} catch (error) {
console.log(error)
const message = (error.response && error.response.data && error.response.data.errMsg)
// navigate('/login')
return rejectWithValue(message)
}
})
How to get the data back the passowrd data inside createAyncThunk?
I've tried to destructure password data object but it didn't help
It looks to me like there is a mistake in the way you are passing the data to the resetPassword action, here:
await dispatch(resetPassword({data, resetToken}))
In this line, you are creating an object with properties data and resetToken instead of passing the data and resetToken as separate arguments to the resetPassword action.
Try changing it to this:
await dispatch(resetPassword(data, resetToken))
i'm not completely sure, but hope it helps! :)
I am building a simple CRUD app, and I have 90% of it working but for some reason I can't get the "edit" functionality to work. When I go to edit my team members name, nothing happens when I click "Edit Name". Worth noting, I copied the code from my "add user" component and modified were needed. Here is my code:
// EDIT USER
import React, { useState, useContext, useEffect } from 'react'
import { GlobalContext } from '../context/GlobalState'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { Form, FormGroup, Label, Input, Button } from 'reactstrap'
const EditUser = (props) => {
const [selectedUser, setSelectedUser] = useState({
id: '',
name: ''
});
const { users, editUser } = useContext(GlobalContext);
const navigate = useNavigate();
const currentUserId = useParams(props);
useEffect(() => {
const userId = currentUserId;
const selectedUser = users.find((user) => user.id === userId);
if (selectedUser) {
setSelectedUser(selectedUser);
}
}, [currentUserId, users]);
const onSubmit = (e) => {
e.preventDefault()
editUser(selectedUser)
navigate('/');
}
const onChange = (e) => {
setSelectedUser({...selectedUser, [e.target.name]: e.target.value})
}
return (
<div className='container w-25'>
<Form className='form-control' onSubmit={onSubmit}>
<FormGroup>
<Label>Edit Name</Label>
<Input type='text' name='name' value={selectedUser.name} onChange={onChange} placeholder='Enter Name'></Input>
</FormGroup>
<Button type='submit'>Edit Name</Button>
<Link to='/' className='btn btn-danger m-2'>Back</Link>
</Form>
</div>
)
}
export default EditUser
// ADD USER
import React, { useState, useContext } from 'react'
import { GlobalContext } from '../context/GlobalState'
import { Link, useNavigate } from 'react-router-dom'
import { v4 as uuid } from 'uuid';
import { Form, FormGroup, Label, Input, Button } from 'reactstrap'
const AddUser = () => {
const [name, setName] = useState('');
const { addUser } = useContext(GlobalContext);
const navigate = useNavigate();
const onSubmit = (e) => {
e.preventDefault()
const newUser = {
id: uuid(),
name: name
}
addUser(newUser);
navigate('/');
}
const onChange = (e) => {
setName(e.target.value);
}
return (
<div className='container w-25'>
<Form className='form-control' onSubmit={onSubmit}>
<FormGroup>
<Label>Name</Label>
<Input
type='text'
name={name}
value={name}
onChange={onChange}
placeholder='Enter Name'></Input>
</FormGroup>
<Button type='submit'>Submit</Button>
<Link to='/' className='btn btn-danger m-2'>Back</Link>
</Form>
</div>
)
}
export default AddUser
// GLOBAL CONTEXT
import React, { createContext, useReducer } from 'react';
import AppReducer from './AppReducer';
const initialState = {
users: []
};
// Create Context \\
export const GlobalContext = createContext(initialState);
// Provider Component \\
export const GlobalProvider = ({children}) => {
const [state, dispatch] = useReducer(AppReducer, initialState);
// Actions
const removeUser = (id) => {
dispatch({
type: 'REMOVE_USER',
payload: id
});
}
const addUser = (user) => {
dispatch({
type: 'ADD_USER',
payload: user
});
}
const editUser = (user) => {
dispatch({
type: "EDIT_USER",
payload: user
});
}
return(
<GlobalContext.Provider value={{
users: state.users,
removeUser,
addUser,
editUser
}}>
{children}
</GlobalContext.Provider>
)
}
// APP REDUCER
export default (state, action) => {
switch(action.type) {
case 'REMOVE_USER':
return {
users: state.users.filter(user => {
return user.id !== action.payload
})
}
case 'ADD_USER':
return {
users: [action.payload, ...state.users]
}
case 'EDIT_USER':
const updateUser = action.payload
const updateUsers = state.users.map(user => {
if(user.id === updateUser.id) {
return updateUser;
}
return user;
})
return {
users: updateUsers
}
default:
return state
}
}
Issue
The issue seems to be that the Edit component isn't accessing the route paths params object correctly, to reference the route path's userId param.
const currentUserId = useParams();
useEffect(() => {
const userId = currentUserId;
const selectedUser = users.find((user) => user.id === userId);
if (selectedUser) {
setSelectedUser(selectedUser);
}
}, [currentUserId, users]);
Here the entire params object is named currentUserId. In the useEffect hook callback this params object is assigned to a local variable userId and then used to find a matching user object in the users array. It's comparing a specific user's id property (a string type) to the entire params object (an object type). This OFC will never be equal and selectedUser is undefined and the local selectedUser state is never initialized to the user you are trying to edit.
Solution
Either access into the params object to correctly access the specific parameter:
const currentUserId = useParams();
useEffect(() => {
const userId = currentUserId;
const selectedUser = users.find((user) => user.id === userId.userId);
if (selectedUser) {
setSelectedUser(selectedUser);
}
}, [currentUserId, users]);
Or just destructure the userId path param directly:
const { userId } = useParams();
useEffect(() => {
const selectedUser = users.find((user) => user.id === userId);
if (selectedUser) {
setSelectedUser(selectedUser);
}
}, [userId, users]);
Suggestion
Initialize the selectedUser state directly. Use a useEffect hook to check if there is a user to edit and issue a back navigation if there is not one to edit.
const EditUser = () => {
const navigate = useNavigate();
const { userId } = useParams();
const { users, editUser } = useGlobalContext(); // *
const [selectedUser, setSelectedUser] = useState(
users.find((user) => user.id === userId)
);
useEffect(() => {
if (!selectedUser) navigate(-1);
}, [navigate, selectedUser]);
const onSubmit = (e) => {
e.preventDefault();
editUser(selectedUser);
navigate("/");
};
const onChange = (e) => {
setSelectedUser({ ...selectedUser, [e.target.name]: e.target.value });
};
return (
<div className="container w-25">
{selectedUser && (
<Form className="form-control" onSubmit={onSubmit}>
<FormGroup>
<Label>Edit Name</Label>
<Input
type="text"
name="name"
value={selectedUser.name}
onChange={onChange}
placeholder="Enter Name"
/>
</FormGroup>
<Button type="submit">Edit Name</Button>
<Link to="/" className="btn btn-danger m-2">
Back
</Link>
</Form>
)}
</div>
);
};
* Note: This was just a custom hook created for convenience in the context code:
const useGlobalContext = () => useContext(GlobalContext);
I'm building a react app using redux for state management.
Inside the SearchField component I am using useReducer for handle the search field inputs (cityField and countryField).
When i submit the fetchData function runs and sends a request to an API.
the SearchField component code:
import React, { useReducer } from "react";
import { connect } from 'react-redux';
import { SET_CITY_FIELD, SET_COUNTRY_FIELD } from '../../redux/search-field/search-field.types';
import { reducer, INITIAL_STATE } from "../../redux/search-field/search-field.reducer";
import { fetchData } from '../../redux/weather-api-data/data.actions';
import { SearchFieldContainer, SearchInput, OptionalField, FormComponent } from './search-field.styles';
const SearchField = () => {
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
const { cityField, countryField } = state;
const onFormSubmit = e => {
e.preventDefault()
cityField.length < 1 ?
alert('Please insert a city')
:
fetchData(cityField, countryField);
}
return (
<SearchFieldContainer>
<FormComponent onSubmit={onFormSubmit}>
<SearchInput type="search" placeholder="Search City" aria-label="Search"
onChange={event => dispatch({ type: SET_CITY_FIELD, payload: event.target.value })}
/>
</FormComponent>
<FormComponent className='country-form' onSubmit={onFormSubmit}>
<SearchInput className='country' type="search" placeholder="Country" aria-label="Search"
onChange={event => dispatch({ type: SET_COUNTRY_FIELD, payload: event.target.value })}
/>
<OptionalField>OPTIONAL</OptionalField>
</FormComponent>
</SearchFieldContainer>
)
}
const mapDispatchToProps = dispatch => ({
fetchData: (cityField, countryField) => dispatch(fetchData(cityField, countryField))
})
export default connect(null, mapDispatchToProps)(SearchField);
The problem is that when the OnFormSubmit function is called, the fetchData function does not send any request and there is no errors in the console. The searchField and countryField data are stored correctly (or at least if I show them in the console I get the actual values). The fetchData function was previously located in the App and worked correctly.
I thank in advance anyone who gives me an answer and tries to find the solution.
The fetchData code:
export const fetchData = (cityField, countryField) => {
return (dispatch) => {
dispatch(fetchCurrentDataRequest())
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${cityField},${countryField}&units=metric&appid=MY_API_KEY`)
.then(response => {
const currentData = response.data
dispatch(fetchCurrentDataSuccess(currentData));
const { lat, lon } = currentData.coord
dispatch(fetchDailyDataRequest())
axios.get(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&units=metric&exclude=current,minutely,hourly,alerts&appid=MY_API_KEY`)
.then(response => {
const dailyData = response.data.daily
dispatch(fetchDailyDataSuccess(dailyData))
})
.catch(error => {
const errorMessage = error.message
dispatch(fetchCurrentDataFailure(errorMessage))
})
})
.catch(error => {
const errorMessage = error.message
alert('No results found')
dispatch(fetchCurrentDataFailure(errorMessage))
})
}
I resolved using useDispatch hook instead of mapdispatchToProps, so the new code is:
import React, { useReducer } from "react";
import { useDispatch } from 'react-redux';
import { fetchData } from '../../redux/weather-api-data/data.actions';
import { reducer, INITIAL_STATE } from '../../redux/search-field/search-field.reducer';
import { SET_CITY_FIELD, SET_COUNTRY_FIELD } from '../../redux/search-field/search-field.types';
import { SearchFieldContainer, SearchInput, OptionalField, FormComponent } from './search-field.styles';
const SearchField = () => {
const dispatchData = useDispatch();
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
const { cityField, countryField } = state;
const onFormSubmit = e => {
e.preventDefault()
cityField.length < 1 ?
alert('Please insert a city')
:
dispatchData(fetchData(cityField, countryField));
}
return (
<SearchFieldContainer>
<FormComponent onSubmit={onFormSubmit}>
<SearchInput type="search" placeholder="Search City" aria-label="Search"
onChange={event => dispatch({ type: SET_CITY_FIELD, payload: event.target.value })}
/>
</FormComponent>
<FormComponent className='country-form' onSubmit={onFormSubmit}>
<SearchInput className='country' type="search" placeholder="Country" aria-label="Search"
onChange={event => dispatch({ type: SET_COUNTRY_FIELD, payload: event.target.value })}
/>
<OptionalField>OPTIONAL</OptionalField>
</FormComponent>
</SearchFieldContainer>
)
}
export default SearchField;
I have multiple forms and buttons which user can edit now I would like to display a button save if the state of redux changes.
live demo : display button save when the state changes
Here is my redux.
const initialState = {
firstName: "Kunta ",
lastName: "Kinte",
age: 35,
country: "Ghana",
color: "#000"
};
const DetailsReducer = (state = initialState, action) => {
const { name, value } = action;
return { ...state, [name]: value };
};
export default DetailsReducer;
Here is my js code to show save button if there is a change in redux state
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
const Settings = () => {
const fields = useSelector((state) => state);
const dispatch = useDispatch();
const [saveBtn, setSaveBtn] = useState(false);
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, []);
console.log(fields.firstName);
return (
<div>
<div>
<h1>Edit </h1>
First Name:{" "}
<input
name="firstname"
value={fields.firstName}
onChange={(e) =>
dispatch({ name: "firstName", value: e.target.value, type: "" })
}
/>
{saveBtn === true && <button className="btn-save">save </button>}
</div>
</div>
);
};
export default Settings;
[1]: https://codesandbox.io/s/multiple-inputs-kkm6l?file=/src/Components/Settings.js:0-816
What do I need to do to solve this problem.?
Did you try this ?
const fields = useSelector((state) => state.WHATEVER_REDUCER);
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, [fields]);
You can try something like this:
<input
name="firstname"
value={fields.firstName}
onChange={(e) =>
dispatch({ name: "firstName", value: e.target.value, type: "" }, setSaveBtn(true))
}
/>
While also removing:
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, []);
You can do it like this. Remove effect hook, move setSaveBtn to input onChange and after you click save, just set setSaveBtn to false.
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
const Settings = () => {
const fields = useSelector((state) => state);
const dispatch = useDispatch();
const [saveBtn, setSaveBtn] = useState(false);
console.log(fields.firstName);
return (
<div>
<div>
<h1>Edit </h1>
First Name:{" "}
<input
name="firstname"
value={fields.firstName}
onChange={(e) => {
dispatch({ name: "firstName", value: e.target.value, type: "" })
setSaveBtn(true)
}}
/>
{saveBtn === true &&
<button
onClick={() => setSaveBtn(false)}
className="btn-save">save </button>}
</div>
</div>
);
};
export default Settings;
I am trying to print the value of the state value whenever I change the password (using useEffect hook). Although it's working well, whenever I try to change the email, the value of email is also rendering in the console
useEffect(() => {
console.log(values);
}, [values.password]);
but as per my logic should be only rendered whenever the value of password is changed.
Following is the log
As I marked they must not be shown as they are rendering whenever I change the value of email
Following is my code
Form.js
import { useState } from "react";
export const useForm = (initialValues) => {
const [values, setValues] = useState(initialValues);
return [
values,
(e) => {
setValues({
//...values,
[e.target.name]: e.target.value,
});
},
];
};
App.js
import "./App.css";
import { useState, useEffect } from "react";
import { useForm } from "./Form";
const App = () => {
const [values, handelChange] = useForm({ email: "", password: "" });
useEffect(() => {
console.log(values);
}, [values.password]);
return (
<div className="field">
<input
type="email"
name="email"
value={values.email}
onChange={handelChange}
/>
<input
type="password"
name="password"
value={values.password}
onChange={handelChange}
/>
</div>
);
};
export default App;
The only thing you have to change is removing the commented values-destructoring at your useForm-hook:
return [
values,
(e) => {
setValues({
...values, // remove the comment from your code in the question!!
[e.target.name]: e.target.value,
});
},
];
};
The comment causes, that password is removed (you can call the prop password on values, but you get undefined) from the new values-object on every email-input. In the log, you see that, but as you described, only once!
Furthermore, I would change your useForm-hook to:
const useForm = (initialValues) => {
const [values, setValues] = useState(initialValues);
return [
values,
(e) => {
setValues(prevValues => {
return {
...prevValues,
[e.target.name]: e.target.value,
}
});
}
];
};
If the new state is computed using the previous state, you should use the previous state from params. React state updates can be batched, and not writing your updates this way can lead to unexpected results.