I asked this question previously, but with React 0.12 and the JSX changes it brought, I'm now seeking another way to implement this feature.
Here's the code that works in React 0.11:
return React.createClass({
render: function() {
var Tag = React.DOM[this.props.element];
return (
<Tag className='text'>
{this.props.value}
</Tag>
);
}
});
I'm making a component that renders different DOM elements based on the prop passed in called "element". this.props.element will be a value such as "p", "h1" or "h2" etc. While this technically works, I'm seeing a warning message in the console:
Warning: Do not pass React.DOM.p to JSX or createFactory. Use the string "p" instead.
Warning: Do not pass React.DOM.h2 to JSX or createFactory. Use the string "h2" instead.
Need some direction to help interpreting that warning and I can't find good documentation for how to do something like this.
Super simple answer to this (+1 for React 0.12 making things simpler!) but all that needed to be done is remove the React.DOM[] part of the variable definition, passing the string literal:
return React.createClass({
render: function() {
var Tag = this.props.element;
return (
<Tag className='text'>
{this.props.value}
</Tag>
);
}
});
Works beautifully; without any console warnings!
Related
In WebStorm and JetBrains IDEs there's a very handy refactoring tool named "Introduce Parameter" which takes the selected text and converts it to a parameter and replaces any calls to the function with the original selection as a parameter. It is available by selecting an expression inside a function, right clicking, Refactor > Introduce Parameter or through shortcut Ctrl Alt P.
For React functional components, parameters are passed as props which is a JavaScript object that contains all attributes passed to the JSX tag.
Is there a similar refactoring action available to introducing a prop to a functional component?
Example:
Before:
function ChildComponent() {
return <div>Hello World</div>
}
function ParentComponent() {
return <div>
<ChildComponent/>
</div>
}
User selects Hello World in editor and uses shortcut for hypothetical Introduce Prop action
After:
function ChildComponent(props: { helloWorld: string }) {
return <div>{props.helloWorld}</div>
}
function ParentComponent() {
return <div>
<ChildComponent helloWorld={"Hello World"}/>
</div>
}
If a whole tag block is selected instead of a string the type of introduced prop would be JSX.Element.
If this functionality is not available natively through WebStorm, is there functionality to add the refactoring customly or perhaps is there a plugin for it?
I'm trying to implement a simple Onsen Navigator in React.
So far I'm receiving an error 'route is not defined' and I was looking through the examples & docs but I only saw the initialRoute prop was provided, how & where does the route prop generated or something? Cause it seems like its not specified.
Here is my the code of my component:
import React, {PropTypes} from 'react';
import ons from 'onsenui';
import * as Ons from 'react-onsenui';
class SignUp extends React.Component {
constructor(props) {
super(props);
this.state = {
index : 0
};
this.renderPage = this.renderPage.bind(this);
this.pushPage = this.pushPage.bind(this);
}
pushPage(navigator) {
navigator.pushPage({
title: `Another page ${this.state.index}`,
hasBackButton: true
});
this.setState({index: this.state.index++});
}
renderPage(route, navigator) {
return (
<Ons.Page key={route.title}>
<section style={{margin: '16px', textAlign: 'center'}}>
<Ons.Button onClick={this.pushPage}>
Push Page
</Ons.Button>
</section>
</Ons.Page>
);
}
render() {
return (
<Ons.Page key={route.title}>
<Ons.Navigator
renderPage={this.renderPage}
initialRoute={{
title: 'First page',
hasBackButton: false
}}
/>
</Ons.Page>
);
}
};
SignUp.propTypes = {
'data-pageName': PropTypes.string.isRequired
};
export default SignUp;
Is this the right syntax in ES6? Have I missed something?
When using Ons.Navigator in react the two required properties are:
initialRoute - it should be an object.
renderPage - method which receives 2 arguments - route and navigator. The route should be an object similar to the initialRoute one. You provide that object when you are calling pushPage and similar methods.
It seems that you already know these 2, but there still 2 other things which you need to be careful about. They are not directly onsen related, but come up a lot when using react in general.
Whenever you have a list of dom elements (for example an array of Ons.Page tags) each of those should have a unique key property.
Whenever you use a method you need to make sure you are binding it if you need some extra arguments.
It seems you also know these two. So the only thing left is to make sure you follow them.
Your syntax is correct - the only thing missing is the route variable in SignUp.render. Maybe you originally copied the renderPage method and that is how you have a leftover Ons.Page.
If you're not putting the SignUp component inside some other navigator, tabbar or splitter then you don't actually need the Ons.Page in its render method. Those are the only cases when they are needed. If you it happens to have one of those components as a parent then you can just specify the key.
PS: I think there should be a React Component Inspector (something like this) which you can install - then I think you may be able to see the place where the error occurs. I think if you knew on which line the problem was you would have been able to solve it. :)
For me, with the object I was passing to initialRoute(), it needed a props property, which itself was an object with a key property. See the before and after below.
Before fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage}}
renderPage={this.renderPage}
/>
);
}
}
This was causing the following console warning:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `Navigator`.
After fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage, props: {key: 'DataEntryPage'}}}
renderPage={this.renderPage}
/>
);
}
}
Notice that the difference I needed to make was the addition of , props: {key: 'DataEntryPage'}.
Feel free to check out this medium article for more information.
This is regarding non-standard attributes. https://facebook.github.io/react/docs/tags-and-attributes.html
In react I have done this:
React.createElement('div', {image:'blah', etc:'blah'});
I need image and etc to be set on the element with setAttribute, and I need react to use its smarts to maintain it as it changes.
A solution here https://stackoverflow.com/a/21654914/1828637 says to add it on componentDidMount but this is not a solution. The attribute will not be maintained as it changes by the react framework.
Is there anyway to tell react to set the attribute on my custom tags?
In react 16 custom attributes are now possible
// Your code:
<div mycustomattribute="something" />
// React 15 output:
<div />
// React 16 output:
<div mycustomattribute="something" />
react 16 custom attributes
This solution is to build on the linked answer by using the React lifecycle method componentWillReceiveProps to update the DOM element attributes with every change to props. For more information about all the lifecycle methods, see http://facebook.github.io/react/docs/component-specs.html.
(Since componentWillReceiveProps can be called more often than when the props actually change, you may wish to compare the props before actually setting them on the node.)
I've provide fiddle you can play with: https://jsfiddle.net/p4h267bo/ and the relevant part of the code is excerpted here:
var Hello = React.createClass({
componentDidMount() {
this.mirrorProps(this.props);
},
componentWillReceiveProps(nextProps) {
this.mirrorProps(nextProps);
},
mirrorProps(props) {
var node = ReactDOM.findDOMNode(this);
node.setAttribute('name', props.name);
},
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
Another alternative is to change the name of the attribute to something that react supports (such as the data-* attributes) :
render() {
return (
<div data-image='blah' data-etc='blah' />
);
}
link to other supported attributes: https://facebook.github.io/react/docs/dom-elements.html
with React v0.12 the #jsx pragma is gone which means it is no longer possible to output jsx with anything other than the React.METHODNAME syntax.
For my use case I am trying to wrap the React object in another object to provide some convenience methods thus, in my component files, I want to be able to write:
var myConvenienceObject = require('React-Wrapper');
var Component = myConvenienceObject.createSpecializedClass({
render: function () {
return <div />
}
})
However the jsx compiler automatially converts <div /> into React.createElement("div", null)
With older versions of React it was possible to handle this using the pragma at the top of the file. However, since that has been removed, I was wondering if there was any way currently to change the name of the object compiled by jsx so <div /> would be transformed into myConvenienceObject.createElement("div", null)
No, it's no longer possible to use a custom prefix for JSX. If you need to do this, you'll need to modify the JSX transform code, or create a fake React.
var React = require('react'), FakeReact = Object.assign({}, React, {
createElement: function(component, props, ...children){
// ...
// eventually call the real one
return React.createElement(component, props, ...children);
}
});
module.exports = FakeReact;
And then to use it you import the fake react and call it React.
var React = require('fake-react');
// ...
render: function(){ return <div />; }
If you would like to make some elements contains in your myConvenienceObject, you could consider the children props as shown in the doc. But this may need some changes in the myConvenienceObject too, to accept the children.
By the way, i'm not sure where is this createSpecializedClass functions comes from and what it does
I have a use case where I have an Image component that has a required "src" attribute and an optional "link" attribute which looks like this:
var Image = React.createClass({
propTypes: {
link: React.PropTypes.string,
event: React.PropTypes.object,
src: React.PropTypes.string.isRequired
},
handleClick: function(event, link) {
analytics.track(event)
.then(function() {
window.location = link;
});
},
render: function() {
return (
<img className='image' src={this.props.src} onClick={this.handleClick.bind(this, this.props.event, this.props.link)} />
);
} });
If I want to selectively include optional props when I call the Image component, how would I do that elegantly? My initial idea was to do a ternary expression like this, except this is not valid JSX:
render: function() {
return (
<Image src={this.props.src} {this.props.link.hasOwnProperty('value') ? link=this.props.link.value : ''} />
)
}
In the example above "this.props.link" is an object that may or may not contain a property called "value" which includes the hyperlink to browse to when the Image is clicked. Also, rather than simply supplying an empty string as the value for the "link" prop, I would prefer to simply leave it out altogether if there is no link.value present.
My reasoning for this is so that on the Image component I can add the css "img:hover {cursor: pointer;}" only if the img actually links somewhere, as opposed to setting it globally which violates UX rules for my app.
I know that I can simply render the "link" prop inside a ternary where it includes the value of the link if it exists and is an empty string if it doesn't, but for curiousity's sake I wanted to see if there maybe was another way to accomplish this.
I also would like to avoid having to do a bunch of conditional statements that create a lot of redundant JSX code like this:
render: function() {
if (this.props.link.hasOwnProperty('value')) {
return <Image link={this.props.link.value} src={this.props.src.value} />;
} else {
return <Image src={this.props.src.value} />;
}
.... // other optional properties
}
Imagine how out of hand that would get if you have a lot of optional props that you want to leave off...
You seem to be overthinking it.
<Image src={this.props.src} link={this.props.link.value} />
In your components you should usually treat any falsy value as omitted.
if (this.props.link) {
...
}
An exception would be for numbers, or the rare (and best avoided case) where it's a boolean defaulting to true.
A more direct answer would be to use spread (new in 0.12).
var props = {src: this.props.src};
if (this.props.link.hasOwnProperty('value')) {
props.link = this.props.link.value;
}
<Image {...props} />
or
var extraProps = {};
if (this.props.link.hasOwnProperty('value')) {
extraProps.link = this.props.link.value;
}
<Image src={this.props.src} {...extraProps} />