how to use setProps in react.js - reactjs

I want to call setProps from outside of myComponent to be able to dynamically change data for myComponent.
I expect that after changing props of the component, it will re-render itself.
I was trying the following:
var myComponent = React.createClass({
render: function () {
return (
React.DOM.div(null, this.props.data)
);
}
});
React.renderComponent(
myComponent({ data: someData }),
document.getElementById('predictionContent')
);
myComponent.setProps({data: someData2});
The problem is that I don't understand how to use setProps for the component.
In my case, I receive "undefined" error.
How to solve this?

warning: setProps is now deprecated.
React.createClass returns a class, it is React.renderComponent which returns an instance of the class which has the setProps method.
Try this instead:
var MyComponent = React.createClass({
render: function () {
return React.DOM.div(null, this.props.data);
}
});
var myComponent = React.renderComponent(
new MyComponent({ data: someData }),
document.getElementById('predictionContent')
);
myComponent.setProps({ data: someData2 });
Having said that, Chad Scira's answer is also correct: probably better to re-render than to call setProps. That will keep it looking the same as the code inside the render() method, and you'll always be able to call renderComponent, regardless of whether it is the first time or a subsequent update.
Like this:
var MyComponent = React.createClass({
render: function () {
return React.DOM.div(null, this.props.data);
}
});
React.renderComponent(
new MyComponent({ data: someData }),
document.getElementById('predictionContent')
);
// later
React.renderComponent(
new MyComponent({ data: someData2 }),
document.getElementById('predictionContent')
);

You're not supposed to do that. Instead just run the renderComponent method again like this:
React.renderComponent(
myComponent({ data: someData2 }),
document.getElementById('predictionContent')
);
react will automatically resolve the differences

Related

ReactJS access "this" in a callback

Within a callback, I'd like to get the props passed to a component but can't get it through this.props as this is undefined there.
Here is a simplified example:
var MyComponent = React.createClass({
options:{
componentFunction: function(c) {
console.log(this.props.myProp); //this references to the options here, not the component itself
}
},
render: function() {
return (
<OtherComponent options={ this.options } />
);
}
});
And I pass the props this way:
<MyComponent myProp={"x"};
Would appreciate any help,
Thanks.
The issue is that componentFunction has its own scope. You need to bind it, which you can do by adding the following method to MyComponent:
componentWillMount: function() {
this.options.componentFunction = this.options.componentFunction.bind(this);
}
Update: If you're using ES6 classes, the above should go in the constructor instead.
However, it may be nicer to use arrow functions instead, which do not define their own scope and so will inherit this from the parent scope.
options:{
componentFunction: () => console.log(this.props.myProp)
}
Use this.options.bind(this) instead of this.options to access this inside the function.
Or use ES6 syntax - options = () = {...}.

Rendering Firebase Data in React

I'm looking to render some firebase data to the HomeFeed component. I update the state in the componentDidMount method. You can see what that looks like below. It's an array. Should I just map over that using the map function? How do I access the specific info like "title", "link", "type", etc. to be able to render it?
Thanks a lot!
var React = require('react');
var Rebase = require('re-base');
var base = Rebase.createClass("https://nimbus-8ea70.firebaseio.com/");
// TODO: Render Firebase data to screen.
// Home
// <Home />
var HomeContainer = React.createClass({
render : function() {
return (
<div className="homeContainer">
<HomeFeed />
</div>
);
}
});
// Home Feed
// <HomeFeed />
var HomeFeed = React.createClass({
componentDidMount: function() {
base.fetch('article', {
context: this,
asArray: true,
then(data){
console.log(data);
this.setState({
feed: data
})
}
});
},
getInitialState: function() {
return {
feed: []
}
},
render : function() {
return (
<div className="homeFeed">
{/* Use map function here? */}
</div>
);
}
});
module.exports = HomeContainer;
render will run whenever state has been changed (unless you modify this behavior with, say, shouldComponentUpdate) so as long as you use setState properly your component will automatically update when its state changes.
If you're asking specifically how to turn an array into something that render understands, then yes, map is a very common way to do that. It might look something like this:
render : function() {
return (
<div className="homeFeed">
{this.state.feed.map(function(ea){
return <div>{ea.someProperty}</div>
})}
</div>
);
}
Note that you have to wrap ea.someProperty in curly braces because you're basically inserting JSX inside of a JavaScript expression inside of even more JSX. This kind of nested JSX/Expression/JSX structure is something you'll have to get comfortable with in React I'm afraid.
More about array.map

How to use setState in ReactJS?

In the follow code, I thought the text should update to the new one after 3 seconds:
https://jsfiddle.net/smrfcr9x/1/
var Component = React.createClass({
render: function() {
return React.DOM.span(null, "hello " + this.props.text);
}
});
var aComponent = React.render(
React.createElement(Component, {
text: "abcd"
}),
document.getElementById("app")
);
setTimeout(function() {
console.log("now setting state");
aComponent.setState({
text: "lmno"
});
}, 3000);
How is it actually done?
You should use setProps to replace setState.
Please notice the warning in console from react.js:
Warning: setProps(...) and replaceProps(...) are deprecated. Instead, call render again at the top level.
You may follow that warning.
EDIT:
Sorry for haven't said it clearly just now.
Using setProps to replace setState can do correctly what you want. But the method setProps has been deprecated and will show the warning above. So I think you should follow the waring, once you want to change the props, rerender the component.
You read the text from props not state in the component, so it won't work if you set the state.
EDIT2:
var Component = React.createClass({
render: function() {
return React.DOM.span(null, "hello " + this.props.text);
}
});
var renderComponent = function(text){
React.render(
React.createElement(Component, {
text: text
}),
document.getElementById("app")
);
}
renderComponent('abcd')
setTimeout(function() {
console.log("now setting state");
renderComponent('lmno')
}, 3000);
I'm sorry for not knowing how to share code in jsfiddle. This code work fine in it.
Ok, I think I get it. The code to needs to set a state, and in render, use this.state instead of this.props:
https://jsfiddle.net/smrfcr9x/7/
var Component = React.createClass({
getInitialState: function() {
return {
text: this.props.text,
};
},
render: function() {
return React.DOM.span(null, "hello " + this.state.text);
}
});

How to pass function groups as props in React?

I use this simple React component only for example.
I would like to access this.setState() inside the functions 'working' and 'group.notWorking'.
var myComponent = React.createClass({
getInitialState: function() {
return {};
},
working: function() {
this.setState({ test: true }); //this is myComponent
},
group: {
notWorking: function() {
console.log(this); //this is window
}
},
render: function() {
return (
<div>
<ChildComponent working={this.working} group={this.group}/>
</div>
);
},
});
My question is how do you pass functions grouped in an object, or is there any best practice, to avoid passing all the functions one by one to children components.
You need to pass a bound version of it.
<ChildComponent working={this.working} group={this.group.notWorking.bind(this)}/>
If you want to pass the whole group you need to make it a function which returns an object and bind it:
group: function() {
return {
notWorking: function() {
console.log(this);
}.bind(this)
};
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

React Warning: Failed Context Types: Required context `router` was not specified in `Component`

I'm trying to test a React component that requires the react-router in separately from app.js.
I have a component that does a redirect using the mixin Router.Navigation like so:
var React = require('react'),
Router = require('react-router');
var Searchbar = React.createClass({
mixins : [Router.Navigation],
searchTerm: function(e) {
if (e.keyCode !== 13) {
return;
}
this.context.router.transitionTo('/someRoute/search?term=' + e.currentTarget.value)
},
render: function() {
return (
<div className="searchbar-container">
<input type="search" placeholder="Search..." onKeyDown={this.searchTerm} />
</div>
)
}
});
module.exports = Searchbar;
I tried to write a test for this but ran into a wall. Apart from the fact that I'm unable to test that transitionTo works as expected, I've also encountered this error message in my Jest tests:
Warning: Failed Context Types: Required context router was not specified in Searchbar.
Does anyone know how I can get rid of the warning and bonus question, how I can test that the transition works as expected?
I've done research into this and this conversation on Github here: https://github.com/rackt/react-router/issues/400 is the closest I've found to the problem. It looks like I need to export the router separately but that seems like a lot of overhead to just run component tests without the warning a la https://github.com/rackt/react-router/blob/master/docs/guides/testing.md
Is that really the way to go?
In version 0.13 of React Router, the mixins Navigation and State were deprecated. Instead, the methods they provide exist on the object this.context.router. The methods are no longer deprecated, but if you're using this.context.router explicitly you don't need the mixin (but you need to declare the contextTypes directly); or, you can use the mixin, but don't need to use this.context.router directly. The mixin methods will access it for you.
In either case, unless you render your component via React Router (via Router#run), the router object is not supplied to the context, and of course you cannot call the transition method. That's what the warning is telling you—your component expects the router to be passed to it, but it can't find it.
To test this in isolation (without creating a router object or running the component through Router#run), you could place a mocked router object on the component's context in the correct place, and test that you call transitionTo on it with the correct value.
Because the router relies heavily on the lesser known context feature of React you need to stub it like described here
var stubRouterContext = (Component, props, stubs) => {
return React.createClass({
childContextTypes: {
makePath: func,
makeHref: func,
transitionTo: func,
replaceWith: func,
goBack: func,
getCurrentPath: func,
getCurrentRoutes: func,
getCurrentPathname: func,
getCurrentParams: func,
getCurrentQuery: func,
isActive: func,
},
getChildContext () {
return Object.assign({
makePath () {},
makeHref () {},
transitionTo () {},
replaceWith () {},
goBack () {},
getCurrentPath () {},
getCurrentRoutes () {},
getCurrentPathname () {},
getCurrentParams () {},
getCurrentQuery () {},
isActive () {},
}, stubs);
},
render () {
return <Component {...props} />
}
});
};
And use like:
var stubRouterContext = require('./stubRouterContext');
var IndividualComponent = require('./IndividualComponent');
var Subject = stubRouterContext(IndividualComponent, {someProp: 'foo'});
React.render(<Subject/>, testElement);
Here is my Jest file for a complete answer to this question. BinaryMuse’s last paragraph got me on the right track but I find code examples always the most helpful, so here it is for future reference.
jest.dontMock('./searchbar');
describe('Searchbar', function() {
var React = require('react/addons'),
Searchbar = require('../../components/header/searchbar'),
TestUtils = React.addons.TestUtils;
describe('render', function() {
var searchbar;
beforeEach(function() {
Searchbar.contextTypes = {
router: function() {
return {
transitionTo: jest.genMockFunction()
};
}
};
searchbar = TestUtils.renderIntoDocument(
<Searchbar />
);
});
it('should render the searchbar input', function() {
var searchbarContainer = TestUtils.findRenderedDOMComponentWithClass(searchbar, 'searchbar-container');
expect(searchbarContainer).toBeDefined();
expect(searchbarContainer.props.children.type).toEqual('input');
});
});
});
Hope this helps someone else in the future.
My answer is not Jest-specific but it might help people coming across the same problem.
I created a class to wrap router context.
Then in your test just add
<ContextWrapper><YourComponent/></ContextWrapper>
It can be useful to wrap other things like ReactIntl.
Note that you will lose the possibility to use shallow rendering but that's already the case with ReactIntl.
Hope that helps someone.
ContextWrapper.js
import React from 'react';
export default React.createClass({
childContextTypes: {
router: React.PropTypes.object
},
getChildContext () {
return {
router: {}
};
},
render () {
return this.props.children;
}
});

Resources