Is there any way to move the jsx from a component's render function to a separate file? If so, how do I reference the jsx in the render function?
You can use react-templates. It gives you exactly this separation between the markup and the component itself, and much more.
I found it very useful for my needs (a large scale web app).
One problem with moving templates into a separate file is that if you use handlers like:
var myTemplate = (
<form onSubmit={this.handleSubmit}></form>
);
and then in your component you use:
render: function() {
return myTemplate;
}
the generated template code will call this.handleSubmit(), so the "this" will be wrong and the handlers won't work. What you need to do is put them in a function, like this:
var myTemplate = function() {
return (
<form onSubmit={this.handleSubmit}></form>
);
};
then in your component's render function, you need to bind it to 'this' correctly, then call it, like this:
render: function() {
return myTemplate.bind(this)();
},
Now you can put that template definition anywhere, in a separate file or however you want to structure and reference your own code. (power to you! Don't listen to these crazy prescriptive frameworks! :) )
Here is a pattern for separating the template jsx that uses CommonJS modules in NodeJS, Browserify or Webpack. In NodeJS, I found the node-jsx module helpful to avoid the need to compile the JSX.
// index.js
require('node-jsx').install({extension: '.jsx'});
var React = require('react'),
Component = require('./your-component');
// your-component.jsx
var YourComponent,
React = require('react'),
template = require('./templates/your-component.jsx');
module.exports = YourComponent = React.createClass({
render: function() {
return template.call(this);
}
});
// templates/your-component.jsx
/** #jsx React.DOM */
var React = require('react');
module.exports = function() {
return (
<div>
Your template content.
</div>
);
};
Update 2015-1-30: incorporated suggestion in Damon Smith's answer to set this in the template function to the React component.
Update 12/2016: the current best practice is to use the .js extension and use a build tool like Babel to output the final javascript from your source. Take a look at create-react-app if you're just getting started. Also, the latest React best practices do recommend a separation between components that manage state (typically called "container components") and components that are presentational. These presentational components can now be written as functions, so they are not far off from the template function used in the previous example. Here is how I would recommend decoupling most of the presentational JSX code now. These examples still use the ES5 React.createClass() syntax.
// index.js
var React = require('react'),
ReactDOM = require('react-dom'),
YourComponent = require('./your-component');
ReactDOM.render(
React.createElement(YourComponent, {}, null),
document.getElementById('root')
);
// your-component.js
var React = require('react'),
YourComponentTemplate = require('./templates/your-component');
var YourComponentContainer = React.createClass({
getInitialState: function() {
return {
color: 'green'
};
},
toggleColor: function() {
this.setState({
color: this.state.color === 'green' ? 'blue' : 'green'
});
},
render: function() {
var componentProps = {
color: this.state.color,
onClick: this.toggleColor
};
return <YourComponentTemplate {...componentProps} />;
}
});
module.exports = YourComponentContainer;
// templates/your-component.js
var React = require('react');
module.exports = function YourComponentTemplate(props) {
return (
<div style={{color: props.color}} onClick={props.onClick}>
Your template content.
</div>
);
};
I just separated JSX into anonymous function files
template.js
export default (component) => {
return <h1>Hello {component.props.name}</h1>
}
my-component.js
import React, {Component} from 'react';
import template from './template';
export default MyComponent extends Component {
render() {
return template(this);
}
}
In template you can access props or state or functions using component variable.
If you don't use any module system, i.e. rely on script tags only, simple expose your JSX component in a global variable and use it when you need :
// component.js
var Component = React.createClass({ /* your component */ });
// main.js
React.renderComponent(Component({}), domNode);
Note : the script tag for component.js must appear before the script tag for main.js.
If you use a Commonjs-like module system like Browserify, simply export your component definition and require it when you need it.
// component.js
var React = require("react");
module.exports = React.createClass({ /* your component */ });
// main.js
var Component = require("component.js");
React.renderComponent(Component({}), domNode);
Related
I have multiple components which all need to do the same thing. (A simple function which maps over their child components and does something to each one). At the moment I am defining this method in each of the components. But I only want to define it once.
I could define it in the top level component and then pass it down as a prop. But that doesn't feel quite right. It is more a library function than a prop. (It seems to me).
What is the correct way of doing this?
Utils.js with latest Javascript ES6 syntax
Create the Utils.js file like this with multiple functions, etc
const someCommonValues = ['common', 'values'];
export const doSomethingWithInput = (theInput) => {
//Do something with the input
return theInput;
};
export const justAnAlert = () => {
alert('hello');
};
Then in your components that you want to use the util functions, import the specific functions that are needed. You don't have to import everything
import {doSomethingWithInput, justAnAlert} from './path/to/Utils.js'
And then use these functions within the component like this:
justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
If you use something like browserify then you can have an external file i.e util.js that exports some utility functions.
var doSomething = function(num) {
return num + 1;
}
exports.doSomething = doSomething;
Then require it as needed
var doSomething = require('./util.js').doSomething;
If you want to manipulate state in helper functions follow this:
Create a Helpers.js file:
export function myFunc(){ return this.state.name; //define it according to your needs }
Import helper function in your component file:
import {myFunc} from 'path-to/Helpers.js'
In your constructor add that helper function to the class
constructor(){ super() this.myFunc = myFunc.bind(this) }
In your render function use it:
`render(){
{this.myFunc()}
}`
Here are some examples on how you can reuse a function (FetchUtil.handleError) in a React component (App).
Solution 1: Using CommonJS module syntax
module.exports = {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
};
Solution 2: Using "createClass" (React v16)
util/FetchUtil.js
const createReactClass = require('create-react-class');
const FetchUtil = createReactClass({
statics: {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
},
render() {
},
});
export default FetchUtil;
Note: If you are using React v15.4 (or below) you need to import createClass as follows:
import React from 'react';
const FetchUtil = React.createClass({});
Source: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass
Component (which reuses FetchUtil)
components/App.jsx
import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {categories: []};
}
componentWillMount() {
window
.fetch('/rest/service/v1/categories')
.then(FetchUtil.handleError)
.then(response => response.json())
.then(categories => this.setState({...this.state, categories}));
}
render() {
return (
<Grid container={true} spacing={16}>
<Grid item={true} xs={12}>
<Categories categories={this.state.categories} />
</Grid>
</Grid>
);
}
}
export default App;
I'll show two styles below, and you'll want to choose depending on how much the components' logic relate to each other.
Style 1 - Relatively related components can be created with callback references, like this, in ./components/App.js...
<SomeItem
ref={(instance) => {this.childA = instance}}
/>
<SomeOtherItem
ref={(instance) => {this.childB = instance}}
/>
And then you can use shared functions between them like this...
this.childA.investigateComponent(this.childB); // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA); // call childB function with childA as arg
Style 2 - Util-type components can be created like this, in ./utils/time.js...
export const getTimeDifference = function (start, end) {
// return difference between start and end
}
And then they can be used like this, in ./components/App.js...
import React from 'react';
import {getTimeDifference} from './utils/time.js';
export default class App extends React.Component {
someFunction() {
console.log(getTimeDifference("19:00:00", "20:00:00"));
}
}
Which to use?
If the logic is relatively-related (they only get used together in the same app), then you should share states between components. But if your logic is distantly-related (i.e., math util, text-formatting util), then you should make and import util class functions.
Another solid option other than creating a util file would be to use a higher order component to create a withComponentMapper() wrapper. This component would take in a component as a parameter and return it back with the componentMapper() function passed down as a prop.
This is considered a good practice in React. You can find out how to do so in detail here.
Sounds like a utility function, in that case why not put it in a separate static utility module?
Otherwise if using a transpiler like Babel you can make use of es7's static methods:
class MyComponent extends React.Component {
static someMethod() { ...
Or else if you are using React.createClass you can use the statics object:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
}
However I don't advise those options, it doesn't make sense to include a component for a utility method.
Also you shouldn't be passing a method down through all your components as a prop it will tightly couple them and make refactoring more painful. I advise a plain old utility module.
The other option is to use a mixin to extend the class, but I don't recommend that as you can't do it in es6+ (and I don't see the benefit in this case).
Shouldn't you use a Mixin for this ? See https://facebook.github.io/react/docs/reusable-components.html
Although they are falling out of favour see https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
Might be useful
I have a situation to convert several jQuery components to VueJs.
In general, I know what to do, but in some cases, I need to replace some functions calls.
For instance:
Component
const Component = (function () {
const initialize = () => {
return 'Tony Stark'
}
return {
initialize: initialize
}
})
export default Component
Random file, using exported function
$( document ).ready(function() {
Component.initialize()
});
What is the best solution to Component.initialize() still working?
Because I have this request in several files.
I got a solution:
import Component from './component'
// Call method
Component.methods.method()
You may import the component to every Vue component and use it like this:
import someComponent from './someComponent'
export default {
created () {
Component.initialize()
}
}
Or you could use instance properties, see https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
I've been getting started with react-redux and finding it a very interesting way to simplify the front end code for an application using many objects that it acquires from a back end service where the objects need to be updated on the front end in approximately real time.
Using a container class largely automates the watching (which updates the objects in the store when they change). Here's an example:
const MethodListContainer = React.createClass({
render(){
return <MethodList {...this.props} />},
componentDidMount(){
this.fetchAndWatch('/list/method')},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(oId){
this.props.fetchObject(oId).then((obj) => {
this._unwatch = this.props.watchObject(oId);
return obj})}});
In trying to supply the rest of the application with as simple and clear separation as possible, I tried to supply an alternative 'connect' which would automatically supply an appropriate container thus:
const connect = (mapStateToProps, watchObjectId) => (component) => {
const ContainerComponent = React.createClass({
render(){
return <component {...this.props} />
},
componentDidMount(){
this.fetchAndWatch()},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(){
this.props.fetchObject(watchObjectId).then((obj) => {
this._unwatch = this.props.watchObject(watchObjectId);
return obj})}
});
return reduxConnect(mapStateToProps, actions)(ContainerComponent)
};
This is then used thus:
module.exports = connect(mapStateToProps, '/list/method')(MethodList)
However, component does not get rendered. The container is rendered except that the component does not get instantiated or rendered. The component renders (and updates) as expected if I don't pass it as a parameter and reference it directly instead.
No errors or warnings are generated.
What am I doing wrong?
This is my workaround rather than an explanation for the error:
In connect_obj.js:
"use strict";
import React from 'react';
import {connect} from 'react-redux';
import {actions} from 'redux/main';
import {gets} from 'redux/main';
import {isFunction, omit} from 'lodash';
/*
A connected wrapper that expects an oId property for an object it can get in the store.
It fetches the object and places it on the 'obj' property for its children (this prop will start as null
because the fetch is async). It also ensures that the object is watched while the children are mounted.
*/
const mapStateToProps = (state, ownProps) => ({obj: gets.getObject(state, ownProps.oId)});
function connectObj(Wrapped){
const HOC = React.createClass({
render(){
return <Wrapped {...this.props} />
},
componentDidMount(){
this.fetchAndWatch()},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(){
const {fetchObject, watchObject, oId} = this.props;
fetchObject(oId).then((obj) => {
this._unwatch = watchObject(oId);
return obj})}});
return connect(mapStateToProps, actions)(HOC)}
export default connectObj;
Then I can use it anywhere thus:
"use strict";
import React from 'react';
import connectObj from 'redux/connect_obj';
const Method = connectObj(React.createClass({
render(){
const {obj, oId} = this.props;
return (obj) ? <p>{obj.id}: {obj.name}/{obj.function}</p> : <p>Fetching {oId}</p>}}));
So connectObj achieves my goal of creating a project wide replacement for setting up the connect explicitly along with a container component to watch/unwatch the objects. This saves quite a lot of boiler plate and gives us a single place to maintain the setup and connection of the store to the components whose job is just to present the objects that may change over time (through updates from the service).
I still don't understand why my first attempt does not work and this workaround does not support injecting other state props (as all the actions are available there is no need to worry about the dispatches).
Try using a different variable name for the component parameter.
const connect = (mapStateToProps, watchObjectId) => (MyComponent) => {
const ContainerComponent = React.createClass({
render() {
return <MyComponent {...this.props} obj={this.state.obj} />
}
...
fetchAndWatch() {
fetchObject(watchObjectId).then(obj => {
this._unwatch = watchObject(watchObjectId);
this.setState({obj});
})
}
});
...
}
I think the problem might be because the component is in lower case (<component {...this.props} />). JSX treats lowercase elements as DOM element and capitalized as React element.
Edit:
If you need to access the obj data, you'll have to pass it as props to the component. Updated the code snippet
I'm trying to use DRY in React JS. I'm trying to use the same HTML partial in different files
partial:
var AdminMenu = React.createClass({
getInitialState: function() {
return {};
},
render: function() {
return (
<h1>Menu</h1>
);
}
});
I'm requeiring it in another file:
require('./scripts/adminMenu.js');
ReactDOM.render(
<AdminMenu/>,
document.getElementById('content')
);
But I'm getting an error:
Uncaught ReferenceError: require is not defined
this scripts are included on html page like:
<script type="text/babel" src="scripts/admin.js"></script>
I'm using webpack
If you are not using any module bundler like webpack or etc.
You should assign you components to some javascript global object, because objects from .jsx are not put in global scope
So here is the solution (used window object here)
Defined module:
window.AdminMenu = React.createClass({
getInitialState: function() {
return {};
},
render: function() {
return (
<h1>Menu</h1>
);
}
});
Where you use it:
ReactDOM.render(
<window.AdminMenu/>,
document.getElementById('content')
);
You have to use
const { Component } = React;
const { render } = ReactDOM;
in place of
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
Consider to use ES6 modules instead of require with React
export a module:
// src/hello.js
// export as default
export default const hello = ({name}) => (
<h1>Hello {name}</h1>
)
import a module:
// src/index.js
// import from relative directory (src)
import Hello from './hello'
const App = () => {
return (
<div>
<Hello name="Pavel" />
</div>
)
}
You should read more about modules for example here: https://www.sitepoint.com/understanding-es6-modules/
The main problems in your existing code are:
It looks, in spite of that you are using Webpack, you use it wrong way. In your final bundle (final JS file), it shouldn't contain any 'require' keywords. Did you use Babel with your webpack? Please, show us your WebPack config.
Your AdminMenu file looks not like module. Your adminMenu file should contain 'export' keyword, after that you will be able to 'require' or 'import' it from other files.
Also, you can write questions in comments with Russian if it is more convenient for you
I'm new to React so please have mercy.
I've also read all of the threads on this, React / JSX Dynamic Component Name and React/JSX dynamic component names in particular. The solutions did not work.
I'm using a tab style interface where a user selects a tab and the appropriate content loads. A parent component stores the tab's content state, passes the corresponding props to the content child. This child then loads the correct content component (as its own child).
var TabbedContent = React.createClass({
loadMenu: function() {
var menus=this.props.carDivState.vehicleDetailState;
for (key in menus) {
if (menus.hasOwnProperty(key)) {
if (menus[key]) {
var Component='TabbedContent'+key;
return <Component />;
}
}
}
},
render: function() {
return (
<div className="TabbedContent">
<div className="contentWrapper">
{this.loadMenu()}
</div>
</div>
)
}
});
loadMenu loops through the props until it finds a true prop. It then returns that key (for instance "Overview") and creates a variable (e.g. Component='TabbledContentOverview').
However, my code returns an HTML tag <tabbedcontentoverview></tabbedcontentoverview>
Question
How do I get React to return the React component instead of an HTML tag? I appear to be using the correct capitalized naming conventions. I've read the Facebook docs. I just don't get it.
https://github.com/vasanthk/react-bits/blob/master/patterns/30.component-switch.md
import HomePage from './HomePage.jsx';
import AboutPage from './AboutPage.jsx';
import UserPage from './UserPage.jsx';
import FourOhFourPage from './FourOhFourPage.jsx';
const PAGES = {
home: HomePage,
about: AboutPage,
user: UserPage
};
const Page = (props) => {
const Handler = PAGES[props.page] || FourOhFourPage;
return <Handler {...props} />
};
// The keys of the PAGES object can be used in the prop types to catch dev-time errors.
Page.propTypes = {
page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
};
First, if you are using Bootstrap for your app, I'd suggest that you use react-bootstrap`s tab. If you are not, I would suggest that you at least take a look at the implementation of their TabPane and TabbedArea.
Here's an example of how it looks like in your app:
const tabbedAreaInstance = (
<TabbedArea defaultActiveKey={2}>
<TabPane eventKey={1} tab='Tab 1'>TabPane 1 content</TabPane>
<TabPane eventKey={2} tab='Tab 2'>TabPane 2 content</TabPane>
<TabPane eventKey={3} tab='Tab 3' disabled>TabPane 3 content</TabPane>
</TabbedArea>
);
React.render(tabbedAreaInstance, mountNode);
Now, back to your question, if you want to create a component by name, just call React.createElement from inside your loadMenu:
loadMenu: function() {
var menus=this.props.carDivState.vehicleDetailState;
for (key in menus) {
if (menus.hasOwnProperty(key)) {
if (menus[key]) {
return React.createElement('TabbedContent'+key);
}
}
}
}
You need to have a reference to an actual class in order to create an element from it (in JS or JSX).
Hold a map of keys to React classes (i.e tabbedChildren), and just create this element using the JS API:
var childComponent = tabbedChildren[key]
return React.createElement(childComponent)
https://facebook.github.io/react/docs/top-level-api.html