I'm setting the state of contact then dispatching the state to the store but the state that is dispatched is empty while the state in my component is updated. This is in the callback too so not sure why it's not updated... am I missing something? Thanks everyone.
if (showForm === true) {
const {contact} = this.state;
this.setState({ contact: shipInfo, showForm: !showForm }, () => {
dispatch(updateContact(contact));
this.formValidation();
});
}
You are calling const { contact } = this.state, then using that version of contact to send to the store. But that is not the updated variable. It sounds like what you want to do is send
dispatch(updateContact(shipInfo));
which is what you're updating your variable to become in the next state.
Another way of writing would be:
this.setState({ contact: shipInfo, showForm: !showForm }, () => {
const { contact } = this.state;
dispatch(updateContact(contact));
this.formValidation();
});
Now you're grabbing the newer version of state, as you're defining it from within your callback, where state has already been updated.
Related
React docs:
Never mutate this.state directly, as calling setState() afterwards
may replace the mutation you made. Treat this.state as if it were
immutable.
That's clear.
class App extends React.Component {
state = {
data: []
}
the following I understand
updateState(event) {
const {name, value} = event.target;
let user = this.state.user; // this is a reference, not a copy...
user[name] = value; //
return this.setState({user}); // so this could replace the previous mutation
}
this following I don't understand
updateState(event) {
const {name, value} = event.target;
let user = {...this.state.user, [name]: value};
this.setState({user});
}
I understand (as in previous example), that I should not either only:
mutate state directly without calling setState; or
mutate it and then use setState afterwards.
However, why can't I just (without direct mutation) call setState without creating a new copy of state (no spread operator/Object.assign)? What would be wrong with the following:
getData = () => {
axios.get("example.com") ...
this.setState({
data:response.data
})
}
Why should it be:
getData = () => {
axios.get("example.com") ...
this.setState({
data:[...data, response.data]
})
}
render (){
...
}
}
What would be wrong with the following:
this.setState({
data: response.data,
});
Absolutely nothing, unless you don't want to replace the contents of this.state.data with response.data.
Why should it be:
this.setState({
data: [...data, response.data],
});
Because with spread you are not loosing the contents of this.state.data - you are basically pushing new response into the data array.
Note: You should use callback inside setState to get access to current data from this.state.
this.setState((prevState) => ({
data: [...prevState.data, response.data],
}));
I am trying to use a context value as a initial state for a consuming component. I am subscribe to the state via useEffect but i am not getting the latest values.
i can avoid doing all these unnecessary things using componentWillMountwithin my provider but its going to be deprecated soon..
This is my context provider when mounted i am checking whether a token session is valid and changes the state accordingly which is being consume by some components.
state = {
userData: [],
isAuthenticated: false,
_defaultLogin: this._defaultLogin,
_setToken: this._setToken,
loginPopUp: false
}
componentDidMount(){
const { history } = this.props;
if(!this._validSession()){
this.setState({
loginPopUp: true,
isAuthenticated: false
})
} else {
try {
const userData = this._getUserData();
this.setState({
userData,
isAuthenticated: true
})
} catch (err) {
this._logOut();
history.push('/')
}
}
}
In one of the consuming component i want to know if some context values was updated and use the latest value.
const authService = useContext(AuthServiceContext) // use the context
const [modalShow, setModalShow] = useState(authService.loginPopUp) // use the context loginpopup value as initial state which is false by default
useEffect(() => {
setModalShow(authService.loginPopUp) // set the modal according to the value
}, authService.loginPopUp) // when provider changes state i am expecting to get the new value
I expect for the setModalShow() to use the latest value of authService.loginPopUp context but it is always false
In my react app I have component named profile, and I am fetching data from server and showing it inside that component. I am using redux and redux-thunk along with axios. With help of mapDispatchToProps function, i am calling redux action for fetching that data when component is mounted and saving it to redux state. After that, using mapStateToProps function i am showing that data on the screen via props. That works fine. Now I want to have possibility to edit, for example, first name of that user. To accomplish that i need to save that data to component state when data is fetched from server, and then when text field is changed, component state also needs to be changed. Don't know how to save data to component sate, immediately after it is fetched.
Simplified code:
const mapStateToProps = (state) => {
return {
user: state.user
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUserData: () => dispatch(userActions.getUserData())
}
}
class Profile extends Component {
state:{
user: {}
}
componentDidMount (){
this.props.getUserData()
// when data is saved to redux state i need to save it to component state
}
editTextField = () => {
this.setState({
[e.target.id]: e.target.value
})
};
render(){
const { user } = this.props;
return(
<TextField id="firstName"
value={user.firstName}
onChange={this.editTextField}
/>
)
}
}
You can use componentDidUpdate for that or give a callback function to your action.
I will show both.
First lets see componentDidUpdate,
Here you can compare your previous data and your present data, and if there is some change, you can set your state, for example if you data is an array.
state = {
data: []
}
then inside your componentDidUpdate
componentDidUpdate(prevProps, prevState) {
if(prevProps.data.length !== this.props.data.length) {
// update your state, in your case you just need userData, so you
// can compare something like name or something else, but still
// for better equality check, you can use lodash, it will also check for objects,
this.setState({ data: this.props.data});
}
}
_.isEqual(a, b); // returns false if different
This was one solution, another solution is to pass a call back funtion to your action,
lets say you call this.props.getData()
you can do something like this
this.props.getData((data) => {
this.setState({ data });
})
here you pass your data from redux action to your state.
your redux action would be something like this.
export const getData = (done) => async dispatch => {
const data = await getSomeData(); // or api call
// when you dispatch your action, also call your done
done(data);
}
If you are using React 16.0+, you can use the static method getDerivedStateFromProps. You can read about it react docs.
Using your example:
class Profile extends Component {
// other methods here ...
static getDerivedStateFromProps(props) {
return {
user: props.user
}
}
// other methods here...
}
I am getting the data from my form component and trying to set the state of my app component with this data.
However, the state.data is an empty object and is not updating the data. I console log the model data before setting it to check if it exists. Their is data within the model.
import React, { Component, Fragment } from "react";
import Form from "../components/Form";
import product from "./product.json";
class App extends Component {
constructor() {
super();
this.state = {
data: {}
};
}
onSubmit = (model) => {
console.log("Outer", model);
this.setState({
data: model
});
console.log("Form: ", this.state);
}
render() {
const fields = product.fields;
return (
<Fragment>
<div>Header</div>
<Form
model={fields}
onSubmit={(model) => {this.onSubmit(model);}}
/>
<div>Footer</div>
</Fragment>
);
}
}
export default App;
setState() is an async call in React. So you won't likely get the updated state value in the next line. To check the updated value on successful state update, you could check in the callback handler.
Change this
onSubmit = (model) => {
console.log("Outer", model);
this.setState({
data: model
});
console.log("Form: ", this.state);
}
to
onSubmit = (model) => {
console.log("Outer", model);
this.setState({
data: model
}, () => {
console.log("Form: ", this.state);
});
}
As per the react docs, setState is an asynchronous call. You can ensure your state has updated to perform a particular action in two ways as shown below:
You can pass the setState a function which will have your current state and props and you the value you return will be your next state of the component.
Keep in mind following:
state is a reference to the component state at the time the change is
being applied. It should not be directly mutated. Instead, changes
should be represented by building a new object based on the input from
state and props.
Following is an example:
this.setState((state, props) => {
//do something
return {counter: state.counter + props.step};
});
You can pass a callback to the setState function as mentioned in Dinesh's
answer. The callback will be executed once the state has been updated successfully hence ensuring you will have the updated state in the call back.
Following is an example:
this.setState({ ...new state }, () => {
// do something
});
Hope it helps.
I just want to add, that if you will do like this its not going to work:
this.setState({things} , console.log(this.state))
You have to pass a refarence to the call back and not the exscutable code itself. If you won't do so, the function will envoke before the state is updated,even you will see the log.
lately i'm facing a tough issue with React/Redux (Thunk): i've created my Store with Action and Reducer properly, in my Component i trigger the Async function in componentDidMount method in order to update the state, But the State doesn't seems to be changing, although it does changed in componentDidUpdate and mapStateToProps functions ! Why ? Here is my code :
export const getAllInterventions = () => {
return dispatch => {
dispatch(getAllDataStart());
axios.get('/interventions.json')
.then(res => {
dispatch(getAllDataSuccess(res.data));
})
.catch(err => {
dispatch(getAllDataFail(err));
});
};
My reducer :
case actionTypes.GET_ALL_INTERVENTIONS_SUCCESS:
return {
...state,
interventions: interventions: action.interventions
};
My Component:
componentDidMount() {
this.props.getAllInterventions();
console.log('DidMount: ', this.props.inter); /*Empty Array Or Undefined */
}
const mapStateToProps = state => {
console.log('mapStateToProps', state);
return {
inter: state.interventions,
error: state.error
};
Your action and state changes are both asynchronous, doing console.log right after will always return before those changes are actually completed.
Your state is being updated, which is why it works when you console.log in the mapStateToProps.
I suggest setting up redux-devtools, you'll be able to easily track your actions/state.
I hope the code can be usefully for you
componentWillReceiveProps(nextProps){
console.log(nextProps.inter)
}