I have a form submit function to call api do something, it will be like:
Form_Submit = (e) => {
//...
Data_Request({
url: "/bill_product_data",
element: this,
data: formData,
cb: (_response) => {
//...
},
fail: (_response) => {
Alert_message('error', _response['message']);
this.setState({
all_cart: _response['data'].data,
}, this.$forceUpdate())
}
})
}
And after the message alert, I expected the component will re-render because all_cart is changed, when I use console.log to print "all_cart" before and after setState, I can see it has been change by setState, but, why the component does not re-render?
Related
I have a spinner but it works while the page is rendering and I also need it to work when I click the LoadMore button, I don't understand how
Here is the link Spinner
You can call this.setState with setting status to pending in first lines of loadMore method, or better call it in fetchImageApi, just like this:
fetchImageApi = () => {
const { searchbar, page } = this.state;
this.setState({status: 'pending'}, () => {
imageApi(searchbar, page)
.then((images) => {
if (images.total === 0) {
this.setState({ error: "No any picture", status: "rejected" });
} else {
this.setState((prevState) => ({
result: [...prevState.result, ...images.hits],
status: "resolved",
page: prevState.page + 1,
searchbar: searchbar,
}));
}
})
.catch((error) => this.setState({ error, status: "rejected" }));
}
};
There is callback as a second argument of setState method, which means that your callback code will be executed right after react updates its state, so you don't have to write await logic for your code.
export default (DrawNav = createStackNavigator(
{
Home: { screen: Home },
QuestionDetail: { screen: QuestionDetail },
QuestionAsk: { screen: QuestionAsk }
},
{
initialRouteName: "Home",
headerMode: "none"
}
));
Home component lists questions and QuestionDetail shows detail information of the questions but here is the problem that i faced, whenever you back to home from QuestionDetail or other component i want to grab the questions and here is what i did in Home component,
componentDidMount() {
this.getQuestions();
}
componentWillReceiveProps() {
this.setState({ questions: [] }, () => {
this.getQuestions();
});
}
getQuestions() {
this.setState({ isLoading: true });
axios.get(`http://${IP_ADDRESS}/api/questions`)
.then(response => {
console.log('response data: ', response.data);
this.setState({ questions: response.data, isLoading: false })
})
.catch((err) => {
this.setState({ isLoading: false });
console.log('QUESTIONS ERR: '+err);
// this.props.history.push('/');
})
}
but componentWillReceiveProps is not called when you navigate from QuestionDetail to Home?
componentWillReceiveProps is triggered only when component prop updates and not on initial render. As the documentation states,
React doesn’t call UNSAFE_componentWillReceiveProps() with initial props during mounting. It only calls this method if some of component’s props may update. Calling this.setState() generally doesn’t trigger UNSAFE_componentWillReceiveProps().
componentWillReceiveProps is deprecated, particularly because it's often misused. For asynchronous actions componentDidMount and componentDidUpdate are supposed to be used instead of componentWillMount and componentWillReceiveProps:
If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.
If same logic is applicable to both hooks, there should be a method to reuse. There's already such method, getQuestions:
componentDidMount() {
this.getQuestions();
}
componentDidUpdate() {
this.getQuestions();
}
getQuestions() {
this.setState({ isLoading: true, questions: [] });
axios.get(`http://${IP_ADDRESS}/api/questions`)
...
}
When I try to access a state variable which is set in ComponentDidMount, react throws an undefined error. This is because I believe when I'm calling the fetch api and setState in ComponentDidMount, the value isn't ready yet (async stuff). Is there a proper way to either delay the render until the setState call is done or some other way to get the state updated fully before render is called?
I think the code below will give you a basic idea how fetch data and render work.
class App extends Component {
state = {
data:{},
loading:true,
error:null,
}
componentDidMount = () => {
fetch('https://example.com/api/article')
.then((response) => {
return response.json();
})
.then((json) => {
this.setState({
data:json,
loading:false,
})
.catch(error => {
this.setState({
error,
loading:false,
})
});
});
}
render() {
const {data,error,loading} = this.state;
if(loading){
return "Loading ..."
}
if(error){
return "Something went wrong."
}
return 'your actual render component or data';
}
}
export default App;
I am running this code:
.then((url) => {
if (url == null || undefined) {
return this.props.image;
} else {
const { image } = this.props;
//entryUpdate is an action creator in redux.
this.props.entryUpdate({ prop: 'image', value: url })
.then(() => {
this.setState({ loading: false });
});
but I get the following error:
How do I format setState() inside an asynchronous function that's called after an action creator?
Any help would be much appreciated!
In order for this to work, your action creator this.props.entryUpdate would need to return a promise for the async work it's doing. Looking at the error message, that does currently not appear to be the case.
You also need to be aware that calling setState() in the asynchronous callback can lead to errors when the component has already unmounted when the promise resolves.
Generally a better way is probably to use componentWillReceiveProps to wait for the new value to flow into the component and trigger setState then.
I placed the .then() function inside of the the if statement. But it should be like this:
.then((url) => {
if (url == null || undefined) {
return this.props.image;
} else {
const { image } = this.props;
this.props.entryUpdate({ prop: 'image', value: url })
}
})
.then(() => {
this.setState({ loading: false });
});
How can I get my test to reflect the new state of my component after calling updateItem on it. I did try rendered.update() but I must be missing on how something works. Thanks.
Method on class:
updateItem (id) {
return updateItem(id)
.then((result) => {
this.setState({
item: {blah:'blah'}
});
}, (error) => {
throw error;
});
}
Test:
it("should update item accordingly", () => {
const rendered = shallow(React.createElement(Item));
const result = rendered.instance().updateItem();
expect(rendered.state('item')).toEqual({blah:'blah'});
})
You have to get the function from the childs prop and call it. As it returns a promise you need to use async/await or return the promise from your test, have a look at docs for more infos. Then test the state of the rendered component.
it("should update item accordingly", async() => {
const rendered = shallow(React.createElement(Item));
await rendered.find('someChild).prop('updateItem')('someId')
expect(item.state).toEqual({
item: {blah:'blah'}
})
})