score:0
Problem
state.user
is used to set the initial value of your component's state. Changes to those props do not change your state after the component is created. They do change the values in your inputs because the initial value was an empty string ''
so you default to showing the value from props
. This is very misleading since those inputs don't reflect the current state.
I bet I could delete at least half of this code but that's besides the point. But take a moment to think about why props.propState === '' ? '' : props.propState
is always exactly the same as just props.propState
.
Solution
I have two key recommendations for how I would rewrite this:
- Select user by id
- Separate into multiple components
- Store only the modifications in the state
Create a selector function selectUserById
that selects a user
from your Redux state by the id
. I don't think it makes sense to store the current user properties as top-level properties of state.user
like you have them right now. It seems like you also have a property state.user.users
which is an array
of all loaded users so I would use that.
const selectUserById = (state, id) => state.user.users.find(user => user.id === id);
Or better yet, store an object of users keyed by id.
const selectUserById = (state, id) => state.user.users[id];
With this approach we either have a complete user object or we have undefined
. It's easy to check for undefined
and not show the "Update User" form at all until we have real data. This makes more sense than using empty strings as the default.
We can access the complete user object from Redux. I would not duplicate that object in state. Instead I would use the state only for the properties that you have changed. You would start out with the state as an empty object and add properties to it as you modify their inputs. You can always combine the two together using object spreading.
const merged = {...existing, ...changes}
Can you implement these suggestions using class components and connect
? Yes. But why add the extra hurdle? Some of the things in your code like this.handleChange.bind(this)
are relics of the past when we had to do that because there wasn't a better way. But now we have better ways so you should use them.
Code
Interactive Demo on CodeSandbox
import "./App.css";
import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "../store";
import { getSingleUser, updateUser } from "../store/slice";
const selectUserById = (state, id) => state.user.users[id];
const UserIdForm = ({ submitId }) => {
const [id, setId] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
submitId(id);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>ID:</label>
<input
type="text"
value={id}
onChange={(event) => setId(event.target.value)}
/>
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
);
};
const UpdateUserForm = ({ id }) => {
const [changes, setChanges] = useState > {};
const existing = useSelector((state) => selectUserById(state, id));
const dispatch = useDispatch();
// respond to changes in id by clearing the changes state and requesting the user
useEffect(() => {
dispatch(getSingleUser(id));
setChanges({});
}, [dispatch, setChanges, id]);
if (!existing) {
return <div>Loading User...</div>;
}
const merged = { ...existing, ...changes };
const handleChange = (property, event) => {
// in function components you have to copy the whole state
setChanges((prevChanges) => ({
...prevChanges,
[property]: event.target.value
}));
};
const handleUpdate = (event) => {
event.preventDefault();
const postData = { ...merged, id };
console.log("POSTDATA:", postData);
dispatch(updateUser(postData));
};
const renderInput = (property, label) => {
return (
<div>
<label>
{label}
<input
type="text"
value={merged[property]} // shows the current value or the updated value
onChange={(event) => handleChange(property, event)}
/>
</label>
</div>
);
};
return (
<form onSubmit={handleUpdate}>
{renderInput("first_name", "First Name:")}
{renderInput("last_name", "Last Name:")}
{renderInput("phone", "Phone:")}
{renderInput("email", "Email:")}
<div>
<input type="submit" value="Submit" />
</div>
</form>
);
};
const UsersContainerUpdate = () => {
// this is the id that was last submitted.
const [id, setId] = useState();
return (
<div>
<div>
<h1>Update User By ID</h1>
<UserIdForm submitId={setId} />
</div>
{!!id && ( // only load when there is an actual id
<div>
<h1>Update User</h1>
<UpdateUserForm id={id} />
</div>
)}
</div>
);
};
export default UsersContainerUpdate;
score:2
If your requirement is just to state after API call inside this.props.getSingleUserData(id),
Approach 1: (Unclean) Add one more argument to getSingleUserData(id, setState) and pass it this.setState as an argument and inside getSingleUserData you can set the state using the function reference passed
Approach 2: You can return a promise from getSingleUserData and do setState once it is resolves
Suggestion: Divide your big component into individual components (like one for getting user ID and one for User data updation). The more we identify and split our project into meanigfull individual components we get more clean codes. Also when you choose to move towards functional components you can reduce lot of boiler plates with hooks.
Source: stackoverflow.com
Related Query
- React JS (Form Update Not Working) - How to set state immediately after getting data from API using mapDispatchToProps?
- How to set the initial state value after fetching data to update a form in react
- useState React Hook set method does not update state immediately in onChange()
- React hooks: Set state 'ModalVisible' but state does not update immediately
- UI not re-rendering on state update using React Hooks and form submission
- How to make React input onChange set state only after onChange stops firing for set time?
- React does not refresh after an update on the state (an array of objects)
- React not re-rendering after array state update
- How to change a react state immediately by setTimeout after setState?
- How to update React component after changing state through redux?
- How to test state update and component rerender after async call in react
- React - How to Update State After AJAX Request
- React Hooks State update one step behind. For this reason my changes are not working correctly
- How does React update a component and its children after a state change?
- React Conditional state update not working
- How to fix the state update in react while getting data from firestore database
- PrevState does not update state value immediately in React JS
- How to update the state immediately in React functional component?
- Django REST and React - JWT Cookie not getting set in browser but working with postman
- React state variable not accurate after set state in useState hook
- React setState not working after deleting data from state copy
- React not re-rendering after state update
- React Hooks: updating state using useState does not update the state immediately
- React lifecycle hooks not working on redux state update
- How to update state after React Router changes automatically?
- React Hooks Wrapper not getting updated after state change in useEffect
- React not re-rending DOM after state update
- TypeScript errors in React Class Component property does not exist on type 'Readonly<{}>', not sure how to set types for state
- React Hooks: using useState inside useEffect doesn't set the state immediately after first rendering
- How to set defaultValue after some delay on react select with react hook form
More Query from same tag
- Implement useSelector equivalent for React Context?
- Unable to resolve "./SafeAreaContext"
- React - Mutating form object for each change . Is that anyway we can do it in a better way
- Dynamic rendering of React components without using evil eval
- How to mock interceptors when using jest.mock('axios')?
- Axios.get returns status code 400 cant figure out whats wrong (SODAapi)
- Trying to emulate Bootstrap navbar collapse/expand with React State
- How can I specify the shape of the state in a React/Redux application?
- How to fix these vulnerabilities by manual review?
- React.js: Find a string in a string and make it clickable if found
- Login hanging when testing cypress with no logs
- Update only one parameter when array is in array React
- React: How to invoke a modal if components are unrelated
- Is there a way to make a dynamic text area in react
- Listing elements from top to bottom rather than horizontally
- Can't understand some codes in redux-form
- react how to pass props to inline css style in components
- Redux/React. After implement the combineReducers cannot get state of app
- Im using mobx 5.15.7 seeing weird behavior
- UseEffect multiple re-renders
- How can I pass routes in Next.js?
- warning in dev console in reactJS application
- How to use hover to display the block from sibling parent
- How do you check if an user has successfully signed in after sending an Ajax request?
- Deep changes of objects cannot be observed in mobx
- Dispatch is not a function error with createSlice()
- Why actions in reduxjs-toolkit are not exported ? (Reactjs - reduxToolkit)
- Is there a way to stream video in react-native?
- JEST Test suite failed to run: TypeError: (0 , _reactRedux.connect) is not a function
- Queue for an api call using react with redux?