Skip to content

Instantly share code, notes, and snippets.

@sebmarkbage
Last active March 15, 2020 00:32
Show Gist options
  • Star 54 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sebmarkbage/ae327f2eda03bf165261 to your computer and use it in GitHub Desktop.
Save sebmarkbage/ae327f2eda03bf165261 to your computer and use it in GitHub Desktop.
Use a factory or JSX

React Element Factories and JSX

You probably came here because your code is calling your component as a plain function call. This is now deprecated:

var MyComponent = require('MyComponent');

function render() {
  return MyComponent({ foo: 'bar' });  // WARNING
}

JSX

React components can no longer be called directly like this. Instead you can use JSX.

var React = require('react');
var MyComponent = require('MyComponent');

function render() {
  return <MyComponent foo="bar" />;
}

Without JSX

If you don't want to, or can't use JSX, then you'll need to wrap your component in a factory before calling it:

var React = require('react');
var MyComponent = React.createFactory(require('MyComponent'));

function render() {
  return MyComponent({ foo: 'bar' });
}

This is an easy upgrade path if you have a lot of existing function calls.

Dynamic components without JSX

If you get a component class from a dynamic source, then it might be unnecessary to create a factory that you immediately invoke. Instead you can just create your element inline:

var React = require('react');

function render(MyComponent) {
  return React.createElement(MyComponent, { foo: 'bar' });
}

In Depth

Read more about WHY we're making this change.

@mtscout6
Copy link

mtscout6 commented Sep 9, 2014

How is this going to effect the output size of application scripts since there will now be added calls for React.createElement against every component? Is the uglified output a negligible difference?

@zpao
Copy link

zpao commented Sep 9, 2014

It should be pretty negligible after minification.

@TrySpace
Copy link

TrySpace commented Nov 7, 2014

I'm getting a whole lot of Warning: This JSX uses a plain function. Only React components are valid in React's JSX transform. in my existing project, and as far as I know I'm following all changes, tried different combos: facebook/react#2436

@mmueller
Copy link

I'm getting this message even though the component is being called inside a JSX block, as this page recommends. What gives?

@dnrahamim
Copy link

I get many of these warnings: "Warning: ______ is calling a React component directly. Use a factory or JSX instead. See: http://fb.me/react-legacyfactory" even though the component is being called inside a JSX block.

The weird thing is that I can't find any underlying pattern for when this warning appears. Even though all of my components are rendered the same way with JSX, react chooses to log a warning for componentA instead of a similar componentB right beneath it.

@ghost23
Copy link

ghost23 commented Dec 2, 2014

I get the same odd warnings. If I look into the generated js code, I see, that the jsx transformer seems to generate the plain component function calls, which this article above discourages, although I have updated the react-tools to 0.12.1.

@benkn
Copy link

benkn commented Dec 4, 2014

Thanks for giving clear explanations for these warnings! Very helpful :)

@mindjuice
Copy link

I'm also seeing the warnings in the same situation as ghost23. I'm only using JSX, no direct calls, and I have react-rools and react 0.12.1.

@darcyadams
Copy link

I must say this is quite disappointing for those of us that have opted out of JSX. I have feared for a while that non-JSX users might start losing out on features, I just hope this isn't the start of that trend.

Can anyone see a reason why the below pattern would be problematic when there is no concern of my modules having to be used with JSX (or by anyone outside my team)?

modules.exports = React.createFactory(MyComponent);

At least here, the module still produces a factory, rather than having to wrap every single require call.

UPDATE: found a definite drawback to this approach, createFactory strips the statics methods from your component. This is a major problem when using react-router, which relies on statics methods for route transition hooks.
UPDATE 2: This wreaks havoc on tests. eg, trying to call methods like getDefaultProps on the class will fail.

modules.exports.factory = React.createFactory(MyComponent);
modules.exports.class = MyComponent;

then...

MyComponentFactory = require('./MyComponent').factory;
// or
MyComponentClass = require('./MyComponent').class;

this at least saves me from the repeatedly calling React.createFactory on the same component. Instead I just require what you need at the moment.

@mlmorg
Copy link

mlmorg commented Dec 11, 2014

@darcyadams I ended up writing a hyperscript syntax wrapper around React.createElement which prevents the unnecessary use of React.createFactory and can also, hopefully, prevent future breaking changes from affecting the interface. -- https://github.com/mlmorg/react-hyperscript

@mikew
Copy link

mikew commented Dec 11, 2014

like @mimorg, I use a couple of methods that allows for these "hyperscript" (haml, jade, whatever you want to call them) strings.

In addition to react-hyperscript, it allows use if a percent sign for refs:

parseTagName("%foo#bar.baz")
// { tagName: 'div', refName: 'foo', id: 'bar', className: 'baz' }

and it has its own createFactory, which is helpful in Array.prototype.map:

var users = [] // from elsewhere
createElement('div', null, users.map(createFactory('div.user--container')))

There is also a store of named components, so you're not limited to the React.DOM items when using string names, which also removes the need for requireing common components in many files.

This is it extracted from the framework we're using at work
https://gist.github.com/mikew/e737273e42ed704c6c54

@Bargs
Copy link

Bargs commented Dec 13, 2014

@ghost23 I saw the same warnings before I upgraded Reactify. The newest version of Reactify uses react-tools v0.12.1 just like you said you're using, but I noticed that the generated JS is now using the new React API (React.createElement) instead of calling the components directly. I would check to make sure the JSX is being compiled with the most up to date tools.

@johdah
Copy link

johdah commented Feb 23, 2015

How would I do this if I use the following way to load my dependencies?

require(['lodash', "jquery", "jquery-ui/ui/dialog", 'react', 'lib/react/forms/selectField'], 
     function(_, jQuery, jQueryDialog, React, SelectField) {

If i use your new way of doing it, it would complain that I load it the wrong way and if I remove the occurrences in the wrapping require call it would complain that it isn't loaded when I try to use:

var SelectField = React.createFactory(require('lib/react/forms/selectField'));

@BoldBigflank
Copy link

I'm getting this warning from the very most basic tutorial code. What is supposed to be different?

/**
 * @jsx React.DOM
 */

var Hello = React.createClass({
    render: function() {
        return <div>Hello {this.props.name}</div>;
    }
});

React.render(<Hello name="World" />, document.getElementById('container'));

@soruban
Copy link

soruban commented Oct 27, 2015

@BoldBigflank, do you still get the warning if you go with es6?

class Hello extends React.Component {
    render() {
        return <div>Hello {this.props.name}</div>;
    }
}

React.render(<Hello name="World" />, document.getElementById('container'));

@arkhamRejek
Copy link

The return sometimes needs to be wrapped in braces

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment