I've actually encountered a similar situation before. I assume the error you encountered was something like this:
Uncaught Error: Invariant Violation: replaceState(...): Can only update a mounted or mounting component.
The error is caused by the fact that, in React
components, you cannot set state before the component is mounted. So, instead of attempting to set the state in componentWillMount
, you should do it in componentDidMount
. I typically add an .isMounted()
check, just for good measure.
Try something like this:
componentDidMount: function () {
request.get(this.fullUrl()).end(function(err, res) {
if (this.isMounted()) {
this.setState({data: res.body});
}
}.bind(this));
}
EDIT: Forgot to mention ... If the component gets "unmounted" before the async operation completes, you may also encounter an error.
This can be easily handled if the async operation is "cancelable". Assuming your request()
above is something like a superagent
request (which are cancelable), I would do the following to avoid any potential errors.
componentDidMount: function () {
this.req = request.get(this.fullUrl()).end(function(err, res) {
if (this.isMounted()) {
this.setState({data: res.body});
}
}.bind(this));
},
componentWillUnmount: function () {
this.req.abort();
}
EDIT #2: In one of our comments you mentioned your intent was to create an isomorphic solution that could load state asynchronously. While this is beyond the scope of the original question, I will suggest you check out
react-async. Out-of-the-box, it provides 3 tools that can help you achieve this goal.
getInitialStateAsync
- this is provided via a mixin, and it allows a component to fetch state data asyncrhonously.
var React = require('react')
var ReactAsync = require('react-async')
var AwesomeComponent = React.createClass({
mixins: [ReactAsync.Mixin],
getInitialStateAsync: function(callback) {
doAsyncStuff('/path/to/async/data', function(data) {
callback(null, data)
}.bind(this))
},
render: function() {
...
}
});
renderToStringAsync()
- which allows you to render server side
ReactAsync.renderToStringAsync(
<AwesomeComponent />,
function(err, markup, data) {
res.send(markup);
})
);
injectIntoMarkup()
- which will inject the server state, along with the markup to ensure it's available client-side
ReactAsync.renderToStringAsync(
<AwesomeComponent />,
function(err, markup, data) {
res.send(ReactAsync.injectIntoMarkup(markup, data, ['./app.js']));
})
);
react-async
provides far more functionality than this. You should check out the react-async documentation for a full list of its features, and a more comprehensive explanation of the ones I briefly describe above.
.bind
, so that should work. @OP: Since you are not usingdocument
, I don't really understand where the error is supposed to come from. – Brunildabruning