For multiple React component , I want to inject a common code to the life cycle of React.
Is there something good way?
var ComponentA = React.createClass({
componentWillMount: function () {
},
componentDidUpdate: function(){
//inject common code
},...
var ComponentB = React.createClass({
componentWillMount: function () {
},
componentDidUpdate: function(){
//inject common code
},...
Do you mean just sharing functions across multiple components? If so, you can just keep them in a separate file and import them where ever you need to:
// common js
function hello() {
console.log('hello');
}
module.exports = hello;
// your component
var hello = require('./common');
var ComponentA = React.createClass({
componentDidUpdate: function(){
hello();
},//...
http://www.webpackbin.com/Nk80m1x_W
Another thing you can do is create a wrapper (higher order) component:
var WrapperComponent = React.createClass({
componentDidUpdate: function() {
// code you want to inject.
},
render: function () {
return(<div>{this.props.children}</div>);
}
});
then whenever you need to use a component with that lifecycle, you can do this in jsx:
<WrapperComponent>
<ComponentA />
</WrapperComponent>
Though higher order component approach suggested by #jzm is better, you could also use mixins:
var myMixin = {
componentWillMount: function(){
//common code
},
componentDidUpdate: function(){
//common code
}
};
var ComponentA = React.createClass({
mixin: [myMixin]
});
var ComponentB = React.createClass({
mixin: [myMixin]
})
Related
I have a JSX file like this:
var Home = React.createClass({
render: function() {
return (
<Test>
...
var Test = React.createClass({
render: function() {
...
module.exports = Home;
But I cant manage that both functions load, I guess I have to add Test to module.exports, but I couldn't find a method that worked.
If you require("home.jsx") it will automaticaly load and render your Test component inside Home.
It would be better to separate those components in defferent files, that will help you to manage components when your app will be too large.
test.jsx
module.exports = React.createClass({
render: function() {
return <div>Test</div>
}
})
home.jsx
var Test = require('./test.jsx');
module.exports = React.createClass({
render: function() {
return <div>
<Test>
</div>
}
})
Of course you are also able to do something like #Mukesh Sharma answer.
Thanks
Hope it helps you.
var Home = React.createClass({
render: function() {
return (
<Test>
...
var Test = React.createClass({
render: function() {
...
module.exports = {
"Home": Home,
"Test": Test
}
I have a react component, lets call it as component 1
define([..., /path/component_2.jsx], function(..., Component2) {
var Component1 = React.createClass({
getInitialState: function() {
return {.......};
},
componentDidMount: function() {
.......
dates = ....;
Component2.setState({dates: dates});
}
render: function() { return (<div ...></div>) }
});
}
As you can see, I am calling the Component2.setState in this component. But I am getting an error like setState is not a function. I tried this with defining a custom function instead of setState function in component 2 and calling this function from component 1, but I am getting the same error, 'is not a function'.
And component 2:
define([..., ], function(...) {
var Component2 = React.createClass({
getInitialState: function() {
return {.......};
},
render: function() { return (<div ...></div>) }
});
}
So I guess in reactjs we calls a component only for rendering something (React DOM elements)? and cannot change the component state?
If so how can I change a state of a component from a different component which is not a child or parent of first?
Components don't publicly expose their state. It's also important to remember that the state is scoped to the instance of components, not their definition.
To communicate between components, you could roll your own event subscription service.
var events = new EventEmitter();
// inside Component1
events.emit('change-state', newState);
// inside Component2
events.on('change-state', function(state) {
this.setState(state);
});
However, this will quickly become difficult to manage.
A more sensible solution is to use Flux. It allows you to explicitly manage the state of your entire application and subscribe to changes in different bits of the state, within your components. It's worth trying to wrap your head around it.
The component that wants to communicate should dispatch an action and this action will be responsible for changing something in the stores, your other component should subscribe to that store and can update its state based on the change.
You can use a shared state between the two component.
You can build a "mixin" like that
app.mixins.DateMixin = {
getInitialState: function ()
return {
dates: []
}
}
};
and then in you components you can include this mixins using the mixins array
define([..., /path/component_2.jsx], function(..., Component2) {
var Component1 = React.createClass({
mixins: [app.mixins.DateMixin],
getInitialState: function() {
return {.......};
},
componentDidMount: function() {
.......
dates = ....;
this.setState({dates: dates});
}
render: function() { return (<div ...></div>) }
});
}
define([..., ], function(...) {
mixins: [app.mixins.DateMixin],
var Component2 = React.createClass({
getInitialState: function() {
return {.......};
},
render: function() { return (<div ...></div>) }
});
}
Now your components share the "dates" state and you can change/check it in both of them.
NB: you can also share methods with in the same way by writing into a mixin component.
Edit: I suggest you to visit this website http://simblestudios.com/blog/development/react-mixins-by-example.html
I'm trying to unit test a react component as below:
Component.jsx:
return React.createClass({
render: function(){
return (
<div id='model' ref = 'model' >
<div className = 'container'>
{container}
</div>
</div>
);
}
componentDidMount: function () {
this.props.model.getNext().then(doSth());
React.findDOMNode(this.refs.model).addEventListener('focus', this.scroll);
},
scroll: function() {
// do scrolling.
}
});
So I wanted to test the logic inside componentDidMount, which requires to stub the addEventListener().
The problem is:
to stub it I need to render to get find the domnode by the refs property, but the componentDidMount will be called right after the render, so the stub doesn't work on that case.
Eg: in the unit test:
var eventFunction = {};
mockEventListener: function(event, callback){
eventFunction[event] = callback;
}
var renderer = React.addons.TestUtils.renderIntoDocument(component, props);
sinon.sandbox.create().stub(React.findDOMNode(renderer.refs.model), 'addEventListener', mockEventListener);
eventFunction.focus();
This will complain that the eventFunction doesn't have any property as the stub is called after componentDidMount being executed so it's nevered get stubed.
Is there any way to stub the function before the render or any other approach to test this unit properly?
Is this the correct way to handle the model change event -
a. The handleModelChange function is being passed as onModelChange prop to SubClass.
b. When the model change event triggers, for the re-render to occur, the handler from the SubComponent changes the state of the MainComponent.
var _SomeMixin={
componentWillMount: function() {
this.props.options.model.on("MODEL_CHANGED", this.props.onModelChange);
},
componentWillUnmount: function() {
this.props.options.model.off("MODEL_CHANGED", this.props.onModelChange);
},
/* more mixin functions */
}
var SubComponent = React.createClass({
mixins: [_SomeMixin],
render: function() {
return (
<div>
<!-- ... more elements .. >
</div>
);
}
});
var MainComponent = React.createClass({
getInitialState: function() {
return {MainComponentState: []};
},
handleModelChange: function() {
if (this.isMounted()) {
this.setState({MainClassState: this.props.options.model.toJSON()});
}
},
render: function() {
return (
<SubClass options={this.props.options} onModelChange={this.handleModelChange} />
);
}
});
This is one of possible ways to inform parent component that inner component has been changed. But this approach will lead you to a callbacks hell, that you will have to pass every time.
Better solution is to use some state management library like Moreartyjs
In React JSX it does not appear to be possible to do something like this:
render: function() {
return (
<{this.props.component.slug} className='text'>
{this.props.component.value}
</{this.props.component.slug}>
);
}
I get a parse error: Unexpected token {. Is this not something React
can handle?
I'm designing this component so that under the hood, the values stored in this.props.component.slug will contain valid HTML elements (h1, p, etc.). Is there any way to make this work?
You should not put component slug in curly braces:
var Hello = React.createClass({
render: function() {
return <this.props.component.slug className='text'>
{this.props.component.value}
</this.props.component.slug>;
}
});
React.renderComponent(<Hello component={{slug:React.DOM.div, value:'This is my header'}} />, document.body);
Here is a working fiddle: http://jsfiddle.net/kb3gN/6668/
Also, you can find JSX Compiler helpful for debugging these kind of errors:
http://facebook.github.io/react/jsx-compiler.html
As nilgun previously pointed out, the component slug should not be wrapped in curly braces.
If you decide to store it in a variable, make sure it starts with a capital letter.
Here is an example:
var Home = React.createClass({
render: function() {
return (
<div>
<h3>This is an input</h3>
<CustomComponent inputType="input" />
<h3>This is a text area</h3>
<CustomComponent inputType="textarea" />
</div>
);
}
});
var CustomComponent = React.createClass({
render: function() {
// make sure this var starts with a capital letter
var InputType = this.props.inputType;
return <InputType />;
}
});
React.render(<Home />, document.getElementById('container'));
Here's a working fiddle: https://jsfiddle.net/janklimo/yc3qcd0u/
If your intention is to inject the actual component rendered, you can do something like this, which is very convenient for testing, or whatever reason you would want to dynamically inject components to render.
var MyComponentF=function(ChildComponent){
var MyComponent = React.createClass({
getInitialState: function () {
return {
};
},
componentDidMount: function () {
},
render: function () {
return (
<div className="MyComponent">
<ChildComponent></ChildComponent>
</div>
);
}
});
return MyComponent;
};
var OtherComponentF=function(){
var OtherComponent = React.createClass({
getInitialState: function () {
return {
};
},
componentDidMount: function () {
},
render: function () {
return (
<div className="OtherComponent">
OtherComponent
</div>
);
}
});
return OtherComponent;
};
var AnotherComponentF=function(){
var AnotherComponent = React.createClass({
getInitialState: function () {
return {
};
},
componentDidMount: function () {
},
render: function () {
return (
<div className="AnotherComponent">
AnotherComponent
</div>
);
}
});
return AnotherComponent;
};
$(document).ready(function () {
var appComponent = MyComponentF(OtherComponentF());
// OR
var appComponent = MyComponentF(AnotherComponentF());
// Results will differ depending on injected component.
ReactDOM.render(React.createElement(appComponent), document.getElementById("app-container"));
});
Edit: Maybe you forgot to add /** #jsx React.DOM */ at the beginning of js?
You can use React.DOM though:
render: function() {
return React.DOM[this.props.component.slug](null, this.props.component.value);
}
http://jsbin.com/rerehutena/2/edit?html,js,output
I am not a React expert, but I think every component should be construct with a specific tag at the beginning. So it could present a clear purpose itself.
The solution for me was to assign the imported Component to a variable(with CapitalCase) and then render that variable.
Example:
import React, { Component } from 'react';
import FooComponent from './foo-component';
import BarComponent from './bar-component';
class MyComponent extends Component {
components = {
foo: FooComponent,
bar: BarComponent
};
//this is the most important step
const TagName = this.components.foo;
render() {
return <TagName />
}
}
export default MyComponent;