ReactJS access "this" in a callback - reactjs

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 = () = {...}.

Related

ReactJS - Accessing Methods of Children (without using refs)

I am trying to access the methods of a children in the parent Component.
First I wanted to use refs instead of this.props.children, but refs are only accessable when using them IN my component.
When using this, it seems not to be possible:
<Parent>
<Child ref="testChild" />
</Parent>
In my Parent Component I am not able to access this.refs.testChild - because of this I have to access this component with this.props.children.
However: When accessing them with this.props.children I am not able to call methods of the child.
Example:
// Child.jsx
{
customMethod() {},
render() {... some stuff ...}
}
// Parent.jsx
{
callChildrenMethods() {
this.props.children.map((child)=>{
console.log(child.props); // Props Object
console.log(child.customMethod); // Undefined
});
},
render() {return(<div>{this.props.children}</div>)}
}
As you can see: The customMethod is undefined. Is there any simple way to access the methods ? The better way would be to access the children with refs but this is not possible in my case.
this.props.children is opaque, you should iterate using React.Children API. Anyways, here's a fiddle which uses some hackery to invoke the child methods -
https://jsfiddle.net/sukantgujar/4bffu7tw/3/
Basically you need to access the type object of the child which points to the wrapped component instance.
var child = React.Children.only(this.props.children),
childType = child.type,
childProto = child.type.prototype,
childName = childProto.constructor.displayName,
childMethod = childProto.someMethod;
You should let the child know through props:
var Child = React.createClass({
render: function() {
if(this.props.shouldRunChildrensMethod) {
this.childsMethod();
}
return (...);
}
});
var Parent = React.createClass({
getInitialState: function() {
return {
shouldRunChildrensMethod: false
}
},
callChildrenMethods: function() {
this.setState({
shouldRunChildrensMethod: true
});
},
render: function() {
return (
<Child shouldRunChildrensMethod={this.state.shouldRunChildrensMethod} />
);
}
});
You can also view 2 way binding:
https://facebook.github.io/react/tips/communicate-between-components.html
Though this communication will be individually 1 by 1 with each child to parent.

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;
}
});

Accessing methods on children

I Have a component that receives children and renders those children
component = React.createClass({
...some JSX
{this.props.children}
...some JSX
)}
At some point I need to loop around the children and call a method that they expose. However, children are not object instances, but rather React representations.
In essence I want to do something like this:
var children = this.props.children
React.Children.forEach(children, function(child) {
child.someAction()
})
What's the best way of achieving this?
There is one way that I came across on IRC and it involved cloning the children then accessing them by ref. Though it seems kind of convoluted.
// in JSX
{
newChildren = React.Children.map(children, function(child) {
return React.CloneElement(child, {ref: child.ref})
})
}
Then do
childRefs = this.props.children.map(function(child) {
return child.ref
})
var self = this;
childRefs.forEach(function(ref){
self.refs[ref].someAction()
})
Though, this feels like I'm working against React.
In generel you should always try to use to outside-in approach and think of data as immutable.
Providing all children with a shouldValidate flag as a prop and then make all children implement the componentWillReceiveProps function to do the validation, would be the preferred way as I see it.
Components should in my opinion never expose functions/properties to be called from outside as this goes against the idea of React.
An jsx example:
Parent
render: function() {
return this.children.map(Child => {
return <Child shouldValidate={this.state.submitting}/>
});
}
onSubmit: function() {
this.setState({
submitting: true
})
}
Child
componentWillReceiveProps: (nextProps) => {
if (this.props.shouldValidate) {
validate();
}
}

Modify React.js class -- override methods?

I've create a component with React.js
var A = React.createClass({ abc: function () {} })
How can i access abc method? A.prototype.abc is undefined
All methods on React components should be considered private. There are very rare exceptions. The public api of the component is the props it takes.
I decided to store methods in plain js object and to add method toReact. Exmaple:
var Game = {
render: function () {
return (
<PlayersTable data={ this.props.data } />
)
},
toReact: function () {
return React.createClass(this)
}
}

Is it possible to define a ReactJS Mixin method that can be overridden in a React Component?

The Facebook ReactJS library has strict rules about which component methods can be overridden and how. Unless it's specifically allowed, we cannot redefine a method.
For my custom mixins how can I update the SpecPolicy if I have a method I want to allow to be overridden? Is this even possible?
This example is a bit contrived but it should get the point across. Say I have the mixin below which is trying to provide a default renderItem method, intended to be overridden if necessary. When I attempt to render the component <Hello ... /> I get an Invariant Violation error. You can find a jsfiddle here.
var MyMixin = {
render: function () {
return this.renderItem();
},
renderItem: function () {
return <div>Hello {this.props.name}</div>;
}
};
var Hello = React.createClass({
mixins: [MyMixin],
renderItem: function() {
return <div>Hey {this.props.name}</div>;
}
});
This isn't possible right now. It's likely that a future version of React will have mixins that take advantage of ES6 classes and will be a bit more flexible. See here for a proposal:
https://github.com/reactjs/react-future/blob/master/01%20-%20Core/02%20-%20Mixins.js
You could just use something like jQuery extend to extend the object that's passed to React.createClass, instead of using a mixin - this would allow you to still use Mixins when you want, and use this method when you need to (JS Fiddle):
/** #jsx React.DOM */
var MyMixin = {
render: function () {
return this.renderItem();
},
renderItem: function () {
return <div>Hello {this.props.name}</div>;
}
};
var Hello = React.createClass($.extend(MyMixin, {
renderItem: function() {
return <div>Hey {this.props.name}</div>;
}
}));
React.renderComponent(<Hello name="World" />, document.body);
Maybe you can do something like this if you still want a default rednerItem implementation:
var MyMixin = {
render: function () {
return this.renderItem();
},
renderItem: function () {
var customRender = this.customRenderItem;
return customRender != undefined ? customRender() : <div>Hello {this.props.name}</div>;
}
};
var Hello = React.createClass({
mixins: [MyMixin],
customRenderItem: function() {
return <div>Hey {this.props.name}</div>;
}
});
The mixin has access to the component properties and state. You can have multiple implementations in the mixin and use the implement you need based on the properties/state.
Only thing you want to make sure that the components using the mixin have these properties/state or have a default implementation.
In situation, when i need to provide some properties to mixin, that depends on my react class, i make mixin as a function with needed arguments, that return mixin object
//mixin.js
module.exports = function mixin(name){
return {
renderItem(){
return <span>name</span>
}
};
}
//react-class.js
var myMixin = require('mixin');
module.exports = React.createClass({
mixins:[myMixin('test')],
render(){
return this.renderItem();
}
});
//result
<span>test</test>

Resources