How to best clone a class in ReactJS - reactjs

I have a large ReactJS class which I'd like to clone. The scenario is that the original React class is in one batch of code, which I want to largely re-use in another - just with a few small changes. Ideally I was hoping I could do something like this:
var Element1 = React.createClass({
customMethod1: function() { ... },
customMethod2: function() { ... },
render: function () { ... }
});
// clone and override whatever we want
var Element2 = React.cloneClass(Component1);
Element2.customMethod1 = function () { ... };
// now we can use <Element2 />
Any idea?

Try using composition over cloning/inheritance. This is recommended approach with React.
Starting from React 0.12 the new API introduced React.createFactory.
var MyComponentClass = React.createClass(...);
var MyComponent = React.createFactory(MyComponentClass);
var MyOtherComponent = React.createClass({
render: function() {
return MyComponent({ prop: 'value' });
}
});
Another possible way of composition would be to pass react element into another component via refs: https://stackoverflow.com/a/25723635/540802

Related

Understanding React props

Looking at Facebook's react example here, I found this code showing how to use mixins to set intervals. I am confused as to what is happening with this.intervals. I understand that state holds render-altering data, and props handle data handed down from a parent component, ideally. I would have used this.props.intervals instead, but what is the difference between the two?
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // Call a method on the mixin
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
ReactDOM.render(
<TickTock />,
document.getElementById('example')
);
When you use props, you know for 100% certainty the value should will be coming from it's immediate parent component (as a property).
When you see state, you know the value is being born/created within that component it's self.
The key, when state changes, every child below will render if any of their received props change.
Your Mixin is not a normal React class. It is simply an object, so this in the case of this.interval, is a reference to the scope of the object in which the method is being executed - TickTock.

Why is TestUtils.Simulate.click in Jest not working when used directly on React Components?

Let's say I have 2 components. A parent that contains a child.
The child component is a button like so:
var React = require('react');
var ChildButton = React.createClass({
onSubmitAnswer: function(e) {
this.props.onClick(this);
},
render: function() {
return (
<div className={this.props.visibility}>
<button onClick={this.onSubmitAnswer}>Click Me</button>
</div>
)
}
});
module.exports = ChildButton;
It lives within it's parent, which looks like this:
var React = require('react'),
ChildButton = require('./face-submit-button');
var ParentComponent = React.createClass({
onButtonSubmit: function() {
//Something happens here
},
render: function() {
return (
<div>
//Some more components
<ChildButton text="Submit" onClick={this.onButtonSubmit} />
</div>
)
}
});
module.exports = ParentComponent;
So far so good. Everything works as expected in the UI. But I've encountered some issues in the Jest tests using TestUtils.Simulate.click().
My test for the ChildButton component is straightforward and behaves as I would expect.
jest.dontMock('./child-button');
describe('ChildButton', function() {
var React = require('react/addons'),
ChildButton = require('./child-button'),
TestUtils = React.addons.TestUtils;
describe('events', function() {
var button,
onClickStub;
beforeEach(function() {
onClickStub = jest.genMockFn();
button = TestUtils.renderIntoDocument(
<ChildButton onClick={onClickStub} />
);
});
it('should call onSubmitAnswer when the button is clicked', function() {
var buttonTag = TestUtils.findRenderedDOMComponentWithTag(button, 'button');
TestUtils.Simulate.click(buttonTag);
expect(onClickStub).toBeCalled();
});
});
});
My test for the parent component started out looking the same:
jest.dontMock('./parent-component');
describe('ParentComponent', function() {
var React = require('react/addons'),
ParentComponent = require('./parent-component'),
ChildButton = require('./child-button'),
TestUtils = React.addons.TestUtils;
describe('events', function() {
var parent,
onClickStub;
beforeEach(function() {
onClickStub = jest.genMockFn();
parent = TestUtils.renderIntoDocument(
<ParentComponent onClick={onClickStub} />
);
});
it('should call onButtonSubmit when a click is triggered', function() {
var childButton = TestUtils.findRenderedComponentWithType(parent, ChildButton);
TestUtils.Simulate.click(childButton);
expect(onClickStub).toBeCalled();
});
});
});
But this test fails. The only difference I can see between these two tests is that one uses an HTML tag directly and clicks on it, while the other triggers a click on a React component. Can I not use the click event on React components directly? Is my assumption correct?
And if so, is there a way to trigger a click on React components differently in the tests? I tried using SimulateNative but that had the same effect, the onClickStub doesn't get called on click.
There is currently an open bug for this issue: Let ReactTestUtils.Simulate.click work on non-dom components. So the answer is that due to bugs, you can only use Simulate.click on an actual DOM node. So you can workaround the bug by getting the DOM node until it is fixed.

can i use action in Flux to control routes, HOW?

I am writing a authentication module in Flux, actions : auth,auth_success,auth_error. I am thinking when action auth_error occur, the router will go to '/login'. When action, action, auth_success occur, the router will go to '/dashboard'.
But it seems to be wrong because action only goes to dispatcher. I don't know how to do route the callbacks. Maybe check the store value?
You have to mixin your React class with Route.Navigation object, for instace
/** #jsx React.DOM */
var React = require('react');
var Router = require('react-router')
, Navigation = Router.Navigation;
var UserStore = require('user-store');
var YourClass = module.exports = React.createClass({
mixins:[Navigation], //This is very important
getInitialState: function() {
return {};
},
componentWillMount: function(){
UserStore.addChangeListener(this._method);
},
componentWillUnmount: function(){
UserStore.removeChangeListener(this._method);
},
render: function() {
return (
<div>
</div>
);
},
_method: function() {
// From here you can call methods of Navigator Object
this.transitionTo('SomeRouteName'); //This method will render the specified <Route>
}
});
For further information you can check
https://github.com/rackt/react-router/blob/master/docs/api/mixins/Navigation.md
In order to change the route and according to flux architecture, you should call transitionTo from a callback of some User Store you should have.
I added an example to the code, you may customise it to your specific case.
Happy coding!

When should I use getInitialState in a React component

I have a React component that toggles a className when the component is clicked
var Foo = React.createClass({
getInitialState: function() {
return {className: ''}
},
render: function(){
var className = 'bar ' + this.state.className
return React.createElement('div', {className: className, onClick: this.onClick})
},
onClick: function() {
this.setState({className: 'baz'})
}
});
It works fine, but when I am rendering the app server side, I get the following error
Warning: getInitialState was defined on a component, a plain JavaScript class.
This is only supported for classes created using React.createClass.
Did you mean to define a state property instead?
My build step is setup like so
var Foo = require('./Foo');
var factory = React.createFactory(Foo);
module.exports = React.renderToString(factory({}));
Why is what I am doing wrong, and how should it be done?
I am not sure if this helps, but while using fluxible, this is the syntax i used with JSX as part of require component
var app = new Fluxible({
component: React.createFactory(require('./Components/startup.react.jsx'))
});

Testing React Class is Of Type

I'm attempting to test a react class which renders out several other react classes. All working perfectly apart from i'm not totally sure about the best practise in terms of testing this.
Code:
Parent Class:
module.exports = function (deps) {
var MixLink = require('views/components/mixLink')(deps);
return React.createClass({
render: function() {
return (
<div class="packshotData mixesPage" data-view="mixlist">
{
this.props.mixes.map(function (mix) {
return <MixLink mix={mix} />;
})
}
</div>
);
}
});
};
Child Class:
module.exports = function (deps) {
var Link = require('views/components/link')(deps);
var mixLink = React.createClass({
render: function() {
return (
<div className="packshotData-item packshotData-item-overlay">
<Link className="packshotData-item-link" href={this.props.mix.href} data-control="mixplay">
<img className="packshotData-item-image packshotHeadphones" src={this.props.mix.thumbnailHref} />
<div className="packshotData-item-title-overlay">
<span className="packshotData-item-title-text">{this.props.mix.name}</span>
</div>
</Link>
</div>
);
}
});
return mixLink;
};
Test:
describe('mixLinkList Component', function () {
var MixLinkList = require('views/components/mixLinkList')({}),
MixLink = require('views/components/mixLink')({}),
TestUtils = React.addons.TestUtils;
it('is a function', function () {
expect(MixLinkList).to.be.a(Function);
});
it('is create a MixLink for every mix', function () {
var mixes = [
{
href: 'http://mixlink.com/One',
name: "thunderbirds",
thumbnailHref: 'someUri'
},
{
href: 'http://mixlink.com/Two',
name: "captain scarlet",
thumbnailHref: 'someOtherUri'
}
],
renderedMixLinkList,
renderedComponents;
renderedMixLinkList = TestUtils.renderIntoDocument(
<MixLinkList mixes={mixes} />
);
renderedComponents = TestUtils.findAllInRenderedTree(renderedMixLinkList, function (elem) {
return TestUtils.isCompositeComponentWithType(elem, MixLink);
});
expect(renderedComponents.length).to.be(2);
});
});
The test currently fails.
I can achieve my goals by grabbing the DOM node and checking the actual HTML which to me seems messy as the HTML produced by MixLink is the concern of MixLink Class not the MixLinkList class.
What i would like to do is simply interrogate the rendered component and check it has two children of type MixLink. Is that possible?
Thanks in advance!
For the most part your test seems fine. I think the reason it's failing is that MixLinkList is creating its own MixLink component distinct from the one in the test. You create one MixLink component on line 2 of the parent class, and a different one on line 3 of the test. So the rendered list does not contain elements of the test's MixLink, but rather elements of the list's own link component.
Your method of dependency injection is both the problem and the solution. Simply change the parent class to begin:
module.exports = function (deps) {
var MixLink = deps.MixLink;
And the test can then inject the MixLink component like this:
var MixLink = require('views/components/mixLink')({}),
MixLinkList = require('views/components/mixLinkList')({
MixLink: MixLink
}),
TestUtils = React.addons.TestUtils;
You might also consider using a more specific TestUtils methods such as scryRenderedComponentsWithType (docs), which makes the last part of your test read easier:
renderedComponents = TestUtils.scryRenderedComponentsWithType(
renderedMixLinkList,
MixLink
);

Resources