I'm passing some params from a page to the other to make an API call. What i've notice is when i hard code the value i'm suppose to get into the http call, i get the desired results but when i inject the value from the params into the call, if fails to work. so i thought the params wasn't able to pass till i did an alert in the render function and what i realized was the alert prompts twice, the first one is empty and the second prompt brings the value from the previous page, so then meaning in my componentDidMount, the call
state = {
modalVisible: false,
posts : [],
itemID: ''
}
componentDidMount = () => {
const { navigation } = this.props;
this.setState({itemID : navigation.getParam('itemID')})
api.get('/items/'+`${this.state.itemID}`+'/test_items_details/'+`${userID}`+'/posts').then((response) => {
let data = response.data.data
this.setState({posts: data})
console.log(JSON.stringify(this.state.posts))
})
}
As per the docs, it states
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
Your code snippet is trying to access the state variable before it has in fact been updated.
You can do two things here:
Simply use navigation.getParam('itemID') so now your URL becomes /items/${navigation.getParam('itemID')}/test_item_details/${userID}/posts.
Use the setState callback, the snippet is added below.
Snippet:
this.setState({ itemID: navigation.getParam('itemID') }, () => {
// this.state.itemID is now set, you can make your API call.
});
If your only intention to use this.state.itemID is for the URL, I would opt for the first option.
Related
I'm working on a react app and using redux to manage the store variables. The store variables get updated on an API call on the homepage, and are used in api calls in other components as query parameters.
I set the store variables in the homepage component like so:
API.get("EmployerApiGw", "/employer/" + this.state.shadowAccountId).then(resp => {
if (resp.items.length > 0) {
this.setState({
isValid: true,
openDialog: false
});
this.props.updateAccountid(this.state.shadowAccountId);
this.props.updateCompanyId(resp.items[0].companyid);
} else {
this.setState({
isValid: false
});
}
});
I'm having an issue where the components get mounted before the store variables are updated in the api call. This causes the other api calls to fail because the store variables are null at that point.
API.get("EmployerApiGw", "/employer/" + this.props.accountid + "/billingstatements", {
queryStringParameters: { companyid: this.props.companyinfo.companyid }
})
My temporary fix has been to use componentDidUpdate() to call componentDidMount() if the props have changed (I'm passing the store variables as props to the components). This still causes the invalid api calls to run, but re-renders the components once the store variables have updated.
componentDidUpdate(prevProps) {
if (JSON.stringify(prevProps) !== JSON.stringify(this.props)) {
this.componentDidMount();
}
}
This still causes unnecessary api calls to be made, and clutters up the console.
I was wondering if there is a way to make components wait until the store variables have finished updating.
Thank you.
I would put a loading spinner in the child component and have that show by default. Then I would put an if statement around your second API call so it only gets called if the arguments you need have already been initilised. Something like this:
if (this.props.accountid && this.props.companyinfo && this.props.companyinfo.companyid) {
//do api call
}
Then use componentDidUpdate to watch for when the props change. When they are no longer null you can call your API and replace the loading spinner with the rest of the child component you were waiting for API data to render.
I am new to React, so bear with me please. I have a component that calls another component that takes a property. This property will get it's value on a callback of a function, something like this:
render(){
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp))
this.setState({myProp: p})
});
return <MyComponent myProp={this.state.myProp}/>
}
myFunc will or will not make an API request and depending on that will call the callback sooner or later. This seems to work fine when API request is made and the callback takes longer to return. However, when the request is not needed and callback returns instantaneously (or almost) I am getting a Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What am I doing wrong and what is the right way to approach this? Where would be the right place to put this code? Basically what I need is to re-render MyComponenent if this.state.myProp changes
You shouldn't be calling setState inside the render method, you might end up having an infinite loop.
The call to myFunc should be somewhere else (depending on the business logic you have). When the function finishes, it will update the state and then trigger a re-render so MyComponent will get the latest value.
UPDATE
I don't know which conditions will require calling myFunc again, but you can do:
state = {
myProp: null // or some other value that MyComponent can handle as a null state
}
componentDidMount () {
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp)) // This is needed only if you get null back from the callback and you don't want to perform an unnecesary state update
this.setState({myProp: p})
}
}
render(){
const { myProp } = this.state
// You can also do if (!myProp) return null
return <MyComponent myProp={myProp}/>
}
I am trying to generate an array of UI controls as they're being rendered and the resulting state contains duplicate entries. Is there a good way to protect against this?
This code is fired each time a component is rendered. There are multiple tables with similar controls and I'm using this to get the maximum number of rows per control:
const newcontrols: LoadedCtrls = {
"itemCtrlType": controlType,
"rowsCount": numberRows,
};
if (this.state.loadedcontrols.length == 0) {
this.setState({
loadedcontrols: [...this.state.loadedcontrols, newcontrols]
})
}
for (var i = 0; i < this.state.loadedcontrols.length; i++) {
if (this.state.loadedcontrols[i].itemCtrlType == controlType) {
if (this.state.loadedcontrols[i].rowsCount < numberRows) {
this.setState({
loadedcontrols: [...this.state.loadedcontrols.slice(0, i), newcontrols, ...this.state.loadedcontrols.slice(i)]
})
}
}
else {
this.setState({
loadedcontrols: [...this.state.loadedcontrols, newcontrols]
})
}
}
The result in the console is the following:
[{"itemCtrlType":"map1","rowsCount":2},{"itemCtrlType":"map2","rowsCount":3},{"itemCtrlType":"location","rowsCount":5},{"itemCtrlType":"map2","rowsCount":3},{"itemCtrlType":"monitor","rowsCount":7},{"itemCtrlType":"monitor","rowsCount":7}]
I've attempted to use good practices with immutability but it still seems like setState is firing off with duplicates, and I know it is an asynchronous operation. So is there a way to prevent this?
EDIT: The function is triggered each time a component's fetch function outputs data, and the state is set in the component:
.then(data => {
this.setState({
results: data,
loading: false,
}, () => {
this.finishLoad(this.state.controlType, this.state.customerId, data.length);
});
});
The use of setState() can lead to very uncertain behavior; for two reasons:
setState() should not be used as a synchronous call (i.e., you rely on state being updated immediately).
Calling setState() as you are iterating over the state may not have the intended behavior (because of 1.).
You might consider a more general pattern:
let localState=this.state;
// Rely on and modify localState, as desired
this.setState(localState) // Set final state and trigger potential re-render
You may not need to specify the callback in your initial setState() call since state changes are batched and the next render is implicitly invoked.
The following read may be helpful setState() State Mutation Operation May Be Synchronous In ReactJS
I'm trying to use reactjs to update a state, and once it is updated fire an ajax call requesting a new page. Just before the ajax call fires an offset variable is set: var offset = this.state.npp * this.state.page; However I find after clickNextPage() is fired, the value of this.state.page is not updated.
I fundamentally do not understand what is happening here, this appears to be a race condition, because I watch the state change on my render() function with {this.state.page}.
How can I ensure my this.state.page is updated, and then fire findByName()?
clickNextPage: function(event){
console.log("clicked happend")
page = this.state.page;
console.log(page)
page += 1
console.log(page)
this.setState({page: page});
console.log(this.state.page)
this.findByName()
},
JS Console:
clicked happend
0
1
0
setState is asynchronous in that this.state will not be updated right away. The short answer to your quandary is use the callback (the second parameter to setState) which is invoked after the internal state is been updated. For example
this.setState({page: page}, function stateUpdateComplete() {
console.log(this.state.page)
this.findByName();
}.bind(this));
The longer answer is that after you call setState, three functions are called (see here for more details about each https://facebook.github.io/react/docs/component-specs.html):
shouldComponentUpdate this allows you to inspect the previous and new state to determine whether the component should update itself. If you return false, the following functions are not executed (although the this.state will still be updated within your component)
componentWillUpdate this gives you a chance to run any code before the new state is set internally and rendering happens
render this happens between the component "will" and "did" functions.
componentDidUpdate this gives you a chance to run any code after the new state is set and the component has re-rendered itself
When calling this.setState the new state isn't set directly, React puts it in a pending state which by calling this.state can return the old state.
This is because React might batch your state updates and therefore offers no guarantee that this.setState is synchronous.
What you want to do is called this.findByName() within componentDidUpdate, componentWillUpdate or through the callback offered as a second argument to this.setState depending on your use case. Note that the callback to this.setState won't be fired until after your call has passed and the component has re-rendered itself.
Furthermore in your case you could pass a function do this.setState instead of doing a variable dance to increase readability.
this.setState(function (prevState, currentProps) {
return {page: prevState.page + 1}
}, this.findByName);
Using nameless functions this code can be written in a shorter format.
myReactClassFunction = (param) => {
this.setState({
key: value,
},
() => this.myClassFunction());
}
I'd like to call a component's function when network fetch completes.
function callRestApi({config, schema}) {
return axios(config).then((response) => {
if (schema) {
var data = normalize_json(response.data, schema)
response.entities = data.entities
}
return response
})
}
function* fetchEventList(action) {
try {
const response = yield call(callRestApi, action.payload);
// here I want to call a component's method if possible
yield put({type: action.response.action_type_success, response});
} catch (e) {
}
}
I can think of two ways to do this, and wonder if one is prefered over another or if there's a better way?
method1:
I include the component in the action payload so that I can call the method
method2:
on action.response.action_type_success, change redux state.
Then, component's componentWillReceiveProps compare if the state variable changed and calls the method
The second. You are using redux-saga to handle side effects, so keep it that way. You could add a callback to the action as method1 but I wouldn't mix concepts.
If you update the store on success, it will re-render the component and as you said you could check the newly updated prop in componentWillReceiveProps and trigger the function, however, check nextProps instead of this.props (but I bet you already know that).
This way everything flows one way, no callback hell :) + you can easily test the component just by passing a prop.
Although it's not a bad pattern per se, passing callbacks would be bi-directional flow, which breaks the first rule of flux: Unidirectional flow.