I am working on a project using React Router, and I'm having some problems with the data flow.
On every page there is an AJAX call that gets the data for the component. I have been putting them in componentDidMount:
// Below code is written in ES6
componentDidMount(){
$.get(someURL, (data)=>{
this.setState({data:data})
})
}
Although this works on initial load, it does not get called again when the url changes (a manual refresh is needed). I cannot seem to find a proper life cycle to place the AJAX calls.
Someone please enlighten me with the proper approach to getting data in React Router.
After a bit of searching around, this README ultimately solves the problem.
There are 2 solutions outlined in the document:
Use addHandlerKey={true}:
<Route handler={User} path="/user/:userId" addHandlerKey={true} />
Use componentWillReceiveProps instead of componentDidMount.
I ended up using both, componentDidMount for the initial load, componentWillReceiveProps for subsequent ones.
Since they share the same code, I created a new function _updateState and called it in both lifecycles.
My code now:
class Classes extends React.Component {
componentDidMount(){ this._updateState() }
componentWillReceiveProps(){ this._updateState() }
_updateState(){
$.get(/*Some URL*/, (data)=>{
this.setState({data:data})
})
}
}
Related
I have been working with Redux & React for a few months. I usually always use Chrome with no issues. ( Endless bugs actually :) ).
When I started testing in Firefox I ran into an issue which I need some help with ... To know if there is a perfect way at dealing with this ...
Issue
Redux Props for MapStateToProps are not yet available when the constructor gets called, which means I cannot construct my components state in the component constructor. These props become available swiftly afterwards in the render function. At this stage, it is too late because I cannot construct state in the render function (Could somehow work that, but wouldn't be good to approach right ?).
For the moment I am using the componentWillReceiveProps and duplicating my constructor function with one exception
Constructor function
constructor(props){
super(props);
//Loads of code named A
this.state = {state:A};
}
Component Will Receive Props Function
componentWillReceiveProps (){
//Loads of code named A
this.setState({state:A});
}
There may be an issue over overwriting my state here, but for my exact case here, its only displaying data, no UI changes happen... This doesn't appear correct method either way...
I read this article
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
I am not quite sure if I understand this fully. I did experiment with it a little with no working solutions.
Ideally, I need the constructor to pause until all redux store is populated which also doesn't make sense. Props arrays could be empty.
There are discussions on Slack but none seem to address this exactly. I tried googling issue but couldn't find exact issue addressed ...
I need the mapStateToProps props to construct my state. It is looking like I won't be able to do this and will need to totally refactor code to work more solely in the render function with loads of ternary operators and/or making calls to set state from the render function before the render returns.
Any thoughts on this issue?
Daniel
Why do you think you need put the data you get from props into the component state?
As far as using the data there is no difference between the two except that you're more likely to get into trouble if you copy props to state (see link you posted).
const { A } = this.state;
const { A } = this.props;
If the data is coming via an async method then you should accommodate that in your render method.
render() {
const { A } = this.props;
if (!A) {
return <LoadingIndicator />
}
...
}
I'm a beginner both in programming and React and I have to create a functioning google Map single page website. I'm using google-map-react.
I have a parent App.js (containing the call to and a HTML sidebar) and a child Map.js containing the map itself and axios request function.
I'm making axios requests to fetch data from foursquare api. It works without side effects. Then I want to pass those data to my app.js and update the parent state so that I can renderthe locations on the sidebar.
This is the function I used (in Map.js). I had to put the call in componentWillReceiveProps as a last resource because componentDidMount didn't work:
https://jsfiddle.net/kd1yuhe5/
I think this may be the issue, but it's also the only way I found to make the list show:
this.props.updateVenues(this.state.venues)
This is the code from App.js
updateVenues(venues) {
this.setState({
venues: venues,
});
}
Then I called the method like this:
<Map updateVenues={this.updateVenues.bind(this)} />
The code works, venues are shown in the sidebar (if you need the code let me know, but I don't think it's relevant), but the I keep making requests until I exceed quota.
Again: I'm a beginner. I just started 3 months ago.
EDIT:
Here are both components:
Map.js
https://jsfiddle.net/kd1yuhe5/5/
App.js
https://jsfiddle.net/xwzrm4bp/2/
When the state of a React component is updated (and without custom implementation of componentShouldUpdate), it triggers a re render of that component (ie call the render function).
If the props of the children of this component have changed since the last render, they will also re render.
They re render because they have received new props, and this will also call their componentWillReceiveProps function.
Since you are fetching data each time Map will receive props, you are fetching data each time something change (state change) on App.
First in Map.js, this.props.query is assigned to this.state.query.
This looks like an error, as in this case what you want are the new props receceived by componentWillReceiveProps, this is the first argument of this function.
So you should assign props.query to this.state.query instead.
Except that actually you should not:
this.state.query is only used in componentWillReceiveProps, therefore there is no need to put props.query into state.query.
Second since you have both this.props.query from the previous props update and props.query which is the new received query, you have the opportunity to fetch only when the query has actually changed:
// Receive the update query from parent and fetch the data
componentWillReceiveProps(nextProps){
if (this.props.query !== nextProps.query) {
this.fetchData(nextProps.query);
}
}
Now you may ask, "ok but why my Map component was always re rendered, even when its props didn't changed".
But they did:
in App.js
<Map
query={this.state.query}
center={this.state.center}
updateVenues={this.updateVenues.bind(this)}
getClickedMarker={this.getClickedMarker.bind(this)}
/>
By calling this.updateVenues.bind(this) and this.getClickedMarker.bind(this) in the render method, you are creating new values (actually new Function references)for the updateVenues and getClickedMarker props, at each render.
Instead, you should bind these method in the contructor of App:
constructor(props) {
super(props);
this.updateVenues = this.updateVenues.bind(this);
this.getClickedMarker = this.getClickedMarker.bind(this);
....
}
....
<Map
query={this.state.query}
center={this.state.center}
updateVenues={this.updateVenues}
getClickedMarker={this.getClickedMarker}
/>
This may limit your API calls a lot, you may also debounce them.
I have some kind of a wizard where each step has it's own route. Now when a user uses a direct link to the second step route, I would like to redirect him to the first step (using browserHistory.replace).
The problem I face is that I don't really know which stage in the components life cycle it should be performed. I have tried constructor, render, componentWillMount, but all of those do not prevent the component from rendering even if I use router. So what happens is that redirect occurs, but the component from the previous route still gets renders and fails (no data obviously).
Is there any "proper" way of redirecting during the component initialization (I need to check the state)? Or is there any other better way of doing so?
I don't know the "official" recommended way (still interested if someone can find it), but what worked for me is this:
Redirect during componentWillMount
During render use the same condition as in componentWillMount to return null
Example:
class MyComponent extends Component {
componentWillMount() {
if (!this.props.data) {
browserHistory.replace(myRoute);
}
}
render() {
if (this.props.data) {
return (
<div>
...
</div>
);
}
return null;
}
}
In v2/3 you should use the onEnter function to redirect (using the replace function). v2/3 replicates most of React's life cycle functions because it doesn't really use React to render route components. That is to say, <Route>s are just used for configuration and react-router generates an array of components for a given route and renders each individually.
function redirectToStart(nextState, replace) {
if (!nextState.someCondition) {
replace('/initial-page')
}
}
<Route onEnter={redirectToStart} ... />
I set up a new react project and for some reason, the componentDidMount method is not being called.
I have verified this behavior by placing a call to console.log in componentDidMount but I cannot see its output in the console.
Furthermore, this.setState() is not working.
I'm pretty stumped as to why the componentDidMount is not being called. I tried using both React "v0.14.0" and "v0.14.3".
Why is 'componentDidMount' not being called?
Code:
var React = require('react');
var RecipePage = React.createClass({
componentDidMount: function() {
console.log('mounted!');
},
render: function() {
return (
<div>Random Page</div>
);
}
});
module.exports = RecipePage;
This happened to me when I had componentWillMount() defined 2x in the same class. This did not produce a runtime error. I simply removed the second definition and things started working.
So... lo and behold.... I finally got it working(with the help of a back-end expert). The reason why the "componentDidMount" methods weren't firing was because my server was bundling and serving all the react + html stuff on server side. (Only "render" and "getInitialState" methods get called to create the "html" template that gets delivered through the client's browser...but it stops there because it thinks it's finished)
The fix: Find a way to deliver the resulting "compiled" html through the server AND in addition, allow react's own events to be accessible and "fireable" again on the client side. When compiling my "view" file( index.js or index.html ), I included an "Application.start()" script that injects my bundle.js code into the template again. Then in my gulpfile, exported the "Application" variable so the "view" file can access it.
Gahh...pulled my hair out for this. Time to read up on server side vs. client side rendering
In my case, there was another componentDidMount earlier in the code
I have the create-react-app template. I added the ComponentDidMount into the App Component. I put a console.log and an alert into it. It does not fire and there are no errors in React or the browser. I tried in chrome and firefox.
FIX: what worked for me was rebooting my pc. Then it started working. I do not know why, but this resolve the issue.
Update: Not I have an uppercase 'C' it is 'componentDidMount' not 'ComponentDidMount'.. I think it's time for sleep.
In my case, I call componentDidMount() for retrieving data from a service (API call) and on the render function I have components that are supposed to use the data that is returned from this call.
But because the call is Async the render() function is called immediately and then it crashes (many errors like: "TypeError:cannot read property 'XXXX' of undefined" ) then it looks like componentDidMount is not called at all.
To solve this issue I check on render that myData !== null before returning the component itself - You can use generic loader\spinner on this case that will not render internal components that use data before the data that actually retrieve from service.
Example:
componentDidMount() {
const { GetMyDataFromServiceFunc } = this.props;
GetMyDataFromServiceFunc ("150288");
}
render() {
const { details, loading } = this.props;
if (!details)
return (<GenericLoader />);
return (
<FullScreenModal
<Wizard className="order-new-wizard">
<Component1 editable={false} dataToComponent1={details}
goNextStep={this.goNextStep} />
<Component2 values={details.customerDetail} goNextStep={this.goNextStep} />
</Wizard>
</FullScreenModal>
);
}
}
You need to change your module.exports to point at RecipePage not TestPage
module.exports = RecipePage;
For me, the reason was upper case "C" in ComponentDidMount.
Changed it to componentDidMount and it worked
For me I also declared componentDidUpdate in the class after that componentDidMount started firing as expected.
Although you have a console log statement in your componentDidMount function it may not be executing because that function never gets run. If the error occurs before componentDidMount gets called such as in your render function, you won't see the console log. Try commenting out the code in your render function and see if it appears.
In my case, I imported a component without the export default command in the imported file. When I fixed this, componentDidMount started firing...
For me, I had onComponentDidMount() instead of componentDidMount() facepalm
componentDidMount did not fire for me because I mistakenly called it twice. Also, it will not show any console error. Removing one of them worked perfectly.
I have been writing components in React.js recently. I have never had to use methods like componentWillMount and componentDidMount.
render is indispensable. getInitialState and other helper methods I wrote also come in handy. But not the two aforementioned lifecycle methods.
My current guess is that they are used for debugging? I can console.log out inside them:
componentWillMount: function() {
console.log('component currently mounting');
},
componentDidMount: function() {
console.log('component has mounted');
}
Are there any other uses?
componentDidMount is useful if you want to use some non-React JavaScript plugins. For example, there is a lack of a good date picker in React. Pickaday is beautiful and it just plain works out of the box. So my DateRangeInput component is now using Pickaday for the start and end date input and I hooked it up like so:
componentDidMount: function() {
new Pikaday({
field: React.findDOMNode(this.refs.start),
format: 'MM/DD/YYYY',
onSelect: this.onChangeStart
});
new Pikaday({
field: React.findDOMNode(this.refs.end),
format: 'MM/DD/YYYY',
onSelect: this.onChangeEnd
});
},
The DOM needs to be rendered for Pikaday to hook up to it and the componentDidMount hook lets you hook into that exact event.
componentWillMount is useful when you want to do something programatically right before the component mounts. An example in one codebase I'm working on is a mixin that has a bunch of code that would otherwise be duplicated in a number of different menu components. componentWillMount is used to set the state of one specific shared attribute. Another way componentWillMount could be used is to set a behaviour of the component branching by prop(s):
componentWillMount() {
let mode;
if (this.props.age > 70) {
mode = 'old';
} else if (this.props.age < 18) {
mode = 'young';
} else {
mode = 'middle';
}
this.setState({ mode });
}
componentDidMount only runs once and on the client side. This is important, especially if you're writing an isomorphic app (runs on both the client and server). You can use componentDidMount to perform tasks require you to have access to window or the DOM.
From Facebook's React Page
If you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests, perform those operations in this method.
componentWillMount has fewer use cases (I don't really use it), but it differs in that it runs both on the client and server side. You probably don't want to put event listeners or DOM manipulations here, since it will try to run on the server for no reason.
This is an example of an isomorphic web application that makes use of componentWillMount: https://github.com/coodoo/react-redux-isomorphic-example
However, I'm 99% certain that it runs the code inside componentWillMount for no reason on the server side (I think using componentDidMount to ensure it was only run client side would have made more sense) as the code which ensures that fetch promises are fulfilled before rendering the page is in server.js not inside the individual components.
There is discussion on per-component async fetching here: https://github.com/facebook/react/issues/1739 As far as I can tell, there is not a good use case for componentWillMount as far as isomorphism is concerned at least.
In my project which is a dashboarding tool, I have used componentDidMount().
On home page previously saved dashboards appear on the sidebar. I make an ajax call within componentDidMount() of component rendering Homepage, so as to fetch list of dashboards asynchronously after the page has been rendered.
Why React Life Cycle Methods?
Intend to use third-party (Ex D3.js) library with React Component
class Example extends React.component{
constructor(){
// init state
// will be called only once
}
componentWillMount(){
// will be called only once
// will not be triggered when re-rendering
// usually will fetch data that is needed in order
// to render properly from other API
}
shouldComponentUpdate(){
return false
// will not re-render itself after componentDidMount(){}
}
render(){
return (
<div id="chart"></div>
)
}
componentDidMount(){
d3.select(".chart")
.selectAll("p")
// d3.js ........
// d3.js ........
// Usually, this will trigger React to re-render itself,
// but this time will not because we have set
// shouldComponentUpdate to false
}
}
Why React wants to do this?
Since rendering DOM is an expensive operation, React uses the layer of virtual DOM to update only DOM / DOMs that is/are different from previous state.