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
Related
I have a large React app and I have a few components that I would like to completely disable from a config or global level. Is there any kind of global hook that I can use that is called before any component is rendered? If so, I imagine I can check the name of the component and return null if the name is on the disabled list. How would you do this?
There are a lot of ways to do this:
React's Context API allows you pass props through every level of the component tree so you can use them as flags to enable/disable components. Should be used sparingly however.
Higher Order Components are basically just functions that return a component. You could wrap your components in logic to render them as needed.
Or of course you could use a global state manager like redux to set global states.
There are many ways to do this, so, I'll just describe one simple way: using references and updating the states accordingly.
Full working feature hide/showing sandbox online: codesandbox.io ReactJS Feature Hide/Show Demo
Defined are two classes, class Feature extends React.Component and class App extends React.Component. The render() for <Feature/> is...
render() {
if (!this.state.enabled) {
return <div />;
}
return (
<div className="Feature">
<h1>My Feature!</h1>
</div>
);
}
And the option for enabling/disabling a feature in <App /> would handle display/hiding like so...
handleOnClick(e) {
if (e.target.checked) {
this.feature.setState({ enabled: true });
} else {
this.feature.setState({ enabled: false });
}
}
Of course, you need to make sure that <Feature /> has the reference set...
<Feature
ref={instance => {
this.feature = instance;
}}
/>
If you need simplest solution just use browser global vars and check it in render.
render() {
if( window.globalFlag ) return null
return (
<div> feature content...
Drawbacks:
modifying component,
using global scope,
some unnecessary code can be run earlier (f.e. constructor) and later (f.e. componentDidMount).
Use HOCs - wrap your component - connecting with global store using redux or context API.
<FlagsProvider store={flagStore}>
<SomeComponent_1>
<SomeComponent_2>
<FlagsConsumer flag="someFeatureFlag">
<SomeFeatureComponent />
<FlagsConsumer/> connects to store (redux connect would be an inner wrapper - composing HOCs) and conditionally renders <SomeFeatureComponent /> (or null).
Of course HOC can pass received props to wrapped component - it can be functionally transparent.
Don't reinvent the wheel - use some ready module, read tutorials, google for sth suitable.
HOC can also play a role of A/B testing.
Rendering some JSON data into a set of collapsible HTML elements is EASY using a library like renderjson (npm package) for Vanilla JS
const data = { sample: [1, 2, 3, 4], data: { a: 1, b: 2, c: ["hello", null] } };
const rjson = renderjson(data);
document.getElementById('to-render').append(rjson);
In The react world
However, renderjson returns an HTMLElement, NOT A STRING. Please note that I'm not JUST trying to convert HTML strings into react HTML here. I'm trying to convert the HTMLElement into a real ReactElement.
So, I actually managed to do this, but since I'm parsing the HTMLElement into a string, all the onClick event listeners generated by renderjson on the + and - handles (to open or collapse the json) are LOST in the process....
Does anyone have any idea on how to convert a HTMLElement object into React Element, keeping all the original event handlers?
I guess, the real question here is: how can the renderjson package be "repackaged" to become React friendly?
Here is a codesandbox to make it easier to see what I'm talking about here.
Good question, I keep running into similar issues with non-React JS libraries that return DOM elements. These libraries should be ported to React, but if you don't want to do that, you can use findDomNode() to get a DOM (not React) node, and append your HTMLElement there. Please note that in the official docs, its use is discouraged because "it pierces the component abstraction". Anyways, in your example, you could add a wrapper class:
class JsonTreeWrapper extends Component {
componentDidMount() {
const renderedJson = renderjson(this.props.data);
const container = findDOMNode(this);
container.appendChild(renderedJson);
}
render() {
return <div/>;
}
}
what this does is render a div, and when the component mounts it finds the div and adds the HTMLElement generated by your library there. Simple.
In your App, you can use it like this:
class App extends Component {
render() {
return (
<div>
<h1> Inside the react world </h1>
<JsonTreeWrapper data={data}/>
</div>
);
}
}
This way your listeners will be working just fine.
Codesandbox didn't work for me, but you can check a demo here. I hope this helps!
The project has the following reference, which returns a string:
const left = slide.imageLeft; // introLeft
And further renders it inside React Component. But it returns as a string styles.imageLeft and since webpack doest convert it into corresponding bundled class like 914u923asdsajdlj1l23 the styles are not applied.
<div className={`styles.${left}`}> </div>
P.S I did try to eval, but it drops 2 errors.
There is an internal error in the React performance measurement code. Did not expect componentDidMount timer to start while render timer is still in progress for another instance.
And
ReferenceError: styles is not defined
Can you please suggest the possible ways to achieve dynamic class generation for css-loader.
You have to define the style within the render(), or within the component definition, like this
render: function(){
var myStyle = {
// your style rules go here
};
return(
<div style={myStyle}>
{this.props.children}
</div>
)
}
in a way, this is already dynamic, because all you have to do is to change to style and it'll make sure that the component will re-render on update
I've got a component that returns this:
return (
<div className="audio-widget">
<div
className={this.props.className}
onWheel={this.handleWheel}
/>
{controls}
</div>
)
I need to do the equivalent of:
handleWheel(event) {
let $canvas = $('.audio-widget').find('canvas');
[...]
}
The canvas is drawn programatically by a 3rd party script, so I can't just slap an ID on it (especially since this is a component and there are several per page).
Excuse the extreme n00b question, I'm brand new to React. Thanks!
This is a good use case for the ref prop. If you give a component a ref prop, e.g. ref="foo", upon rendering that component will be available as e.g. this.refs.foo. Then you can get the rendered DOM node with React.findDOMNode.
render() {
// ...
return (
<div ref="audioWidget" className="audio-widget">
<div
className={this.props.className}
onWheel={this.handleWheel}
/>
{controls}
</div>
);
}
handleWheel(event) {
let canvas = React.findDOMNode(this.refs.audioWidget)
.querySelector('canvas');
// ...
}
You can learn more about refs here: https://facebook.github.io/react/docs/more-about-refs.html
I think, it's a bad way to direct access 'canvas' with selector, the reason is as follows:
When you component changed, the React will do some diff works, then update your dom with a best way, that means, the node you get with selector may be changed to another node just for 'the best way'.
You can build a 'Child Component', and do something in the 'handleWheel' of the 'Parent Component', then communicate with the 'Child Component' through the 'props' for 'Child Component'
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