Replacing document.createElement() with React.createElement() - javascript

I have a javascript library which uses document.createElement() which creates an HTMLElement
I want to create a react library with the same functionality.
My idea was to replace the document.createElement() with React.createElement() .
So , the original code which was something like
this.myElement = document.createElement("input");
/* some stuff happens here due only after which the followig code is run */
this.myElement.className = "someClassName";
this.myElement.style.display = "block";
this.myElement.appendChild(someElement);
will become
this.myElement = React.createElement("input");
this.myElement.props.className = "someClassName";
this.myElement.props.style.display = "block"; // Props is read only at this point
this.myElement.appendChild(someReactElement); // This doesn't work either
The problem here is that props is a read only property in react , also appendChild doesn't work that way in. So changing it after React.createElement("input") is not possible. I can create the props object before each createElement call based on the original code but it significantly reduces reusability of the existing code base. Is there a way around that can be used to bypass this issue in react ?
Edit : I know I can pass children and props during the time I call React.createElement however , it reduces my ability to copy paste and reuse the existing code base . The question is specific about changing props and appeding child after the React.createElement call

Create a props object and pass that as the 2nd argument of the createElement function. Follow this-
const props = {
className: 'someClassName',
style: {
display: 'block',
width: '100%'
}
}
React.createElement('input', props);

Read the documentation for React.createElement. You can pass the props (and children) in the function.

Related

Using conditional operator inside Element.classList.add()

I'm making an application that uses TypeScript, and as a templating language I'm using Svelte.
That allows me to create DOM elements with classes that can change in realtime according to a variable, thanks to ternary operator. For instance:
<div class="{theme == "dark" ? "bg-black" : "bg-white"}"> Hello </div>
The thing is, my application has to dynamically generate some DOM elements. That makes me create some divs using the following piece of script:
const parentDiv = document.getElementById("parentDiv");
const childDiv = document.createElement("div")
childDiv.classList.add(theme == "dark" ? "bg-black" : "bg-white")
parentDiv.appendChild(childDiv)
In this case, the conditional operator is just calculated when .add() is called, which happens once. There is no "realtime calculation" of the value like in the first method above. How do I handle this ?
If you are not creating the elements via plain Svelte markup you are probably doing something wrong. There are various directives that help, e.g. {#if} and {#each}.
In some cases it makes sense to imperatively add things, but even then, you should add components not plain DOM elements. You can instantiate any component using the client API, e.g.
new MyComponent({ target: document.body })
Anything inside the component then can use the Svelte mechanism that automatically update. If you need to have reactivity from the outside, you can pass a store as a property or import a global store from within/load it from a context.
(Would recommend making the theme a store. Either global or passed through props/contexts.)
Note on contexts with the client API: They have to be passed explicitly; to inherit existing contexts you can use getAllContexts:
// somewhere at top-level
const context = getAllContexts();
// ...
new MyComponent({ ..., context })

How bad is it to change the DOM in react?

I'm a noob react developer, and I'm currently building a MERN App.
My question to the community is, in my project I had to modify the DOM multiple times as shown below:
document.getElementsByTagName('body')[0].style = 'overflow: hidden';
I know that changing the DOM very often is not recommended in React.Js. So is it ok if I did it only to cut the body's scroll-bar?
In React, changing the DOM directly is usually bad because the state of the page should come directly from the state in React.
But React will render inside the body - the body can't be something returned by the JSX inside a React component, so doing
document.body.style = // ..
really is the only way to change the body's style.
The time you wouldn't want to do such a thing would be if the element being changed was being rendered by React, eg:
// some functional component
const clickHandler = () => {
document.querySelector('.foo').style.backgroundColor = 'green';
};
return (
<div className="foo" onClick={clickHandler}>foo</div>
);
because you could instead toggle some state inside the component which changes the returned JSX to include the different style.
That said, your approach of
document.getElementsByTagName('body')[0].style = 'overflow: hidden';
should be reconsidered, if possible:
Use document.body instead of getElementsByTagName
Do you really have to set the style of the whole <body>? It would be more inline with React to set the style of an element React is rendering, if possible - and then you can use the method mentioned above, of using state and the returned JSX instead of using DOM methods.

Can "font-variation-settings" be used in the styled-components .attrs method?

I'm trying to pass a variable "width" to a component using styled components. The value of "width" is a function of the mouse position. Passing it directly into the literal string causes styled-components to create a new class for every mouse movement, which throws a warning.
To resolve it, I'm trying to use the .attrs method to pass a variable style object to the component, but I can't work out how to pass this css property to the component.
I tried:
const StyledComponent = styled.div.attrs((props) => (
{
style: {
fontVariationSettings: `wdth: ${props.width})`,
},
})
)``;
But React doesn't recognise this as a valid property to pass into CSS. Any way around it?
Got it. Typical that it's always straight after posting a question after spending hours trying different stuff. Rather than using font-variation-settings, I can use the individual properties. Width becomes font-stretch.
So what I am looking for is
const StyledComponent = styled.div.attrs((props) => (
{
style: {
fontStretch: `wdth: ${props.width})`,
},
})
)``;

Should Javascript ES6 Classes be Used as React State?

Should ES6 classes be used directly as React state?
I want to define an ES6 class that:
Has member variables that will be displayed on the frontend. (changes to them trigger re-renders)
Has methods that sync those member variables with my backend periodically as they change.
However, calling setState does not appear to diff class members, at least as far as I can tell.
Using the following class:
class Document{
constructor(){
this.title = "";
this.body = "";
}
syncWithDatabase = async () => {
// do some logic to update the database
}
}
And this component:
// import Document from "...";
export default function Sandbox() {
const [document, setDocument] = useState(new Document());
const [renderTrigger, setRenderTrigger] = useState(false);
return (
<div>
<div>{document.title}</div>
<div>{document.body}</div>
<button
onClick={() => {
document.title = 'Some Default Title';
document.body = 'lorem text';
document.syncWithDatabase(); // being able to take this type of action in this way is why I'm trying to use classes.
setDocument(document);
}}
>
Set Canned Data
</button>
<div>Render trigger is: {renderTrigger ? 'true' : 'false'}</div>
<button onClick={() => setRenderTrigger(true)}>Force Render</button>
</div>
);
}
Clicking the first button will set the title and body on the instance of Document held react state, but it will not update the UI.
Clicking the second button to force a re-render in a way that I am confident will work makes the updated members of document render out, even though they didn't when setDocument is called.
Creating a new object with new Document() and passing it setDocument WILL trigger a re-render. So I'm thinking that react isn't doing a deep compare or is seeing that the reference to the Document object has not changed, and therefore not re-rending.
So, is it possible to change an object's members, pass that object to a setState hook and have it update the UI, without creating an entirely new object? Or should I avoid doing what I'm trying to do here?
You can (but probably shouldn't, see below) use an object created by a constructor function (which is what document is in your code) as state. What you can't do is directly modify it as you are here (see the documentation):
document.title = 'Some Default Title'; // <=== INCORRECT
document.body = 'lorem text'; // <=== INCORRECT
document.syncWithDatabase();
setDocument(document); // <=== INCORRECT
Instead, you'd need to create a new document object
const newDoc = new Document();
newDoc.title = 'Some Default Title';
newDoc.body = 'lorem text';
newDoc.syncWithDatabase();
setDocument(newDoc);
That said, when using the useState hook, you're usually better off keeping your state variables discrete (having one for title and one for body), so that changing one doesn't require also changing the other (unless, of course, they always change together). The documentation discusses that here; here's one quote:
...we recommend to split state into multiple state variables based on which values tend to change together.
(their emphasis)

Setting a field directly on the class definition in ReactJS

Is it a good practice to do this in ReactJS?
var Component = React.createClass({
render: function () {
return (<div></div>);
},
field: 'value', // is this safe?
method: function () {
// do something with field
}
})
Before starting to suggest that I should use this.props or this.state, for me it's not the case, because those are fields that do not affect rendering in any way directly, they just work together to control the rendering.
I would like to use the React class as I do with regular javascript 'classes'.
My main concern here is how those fields and methods are handled inside React, and if the fields are set on the instance itself or directly on the prototype, which would not be suitable at all for what I need.
I ran a quick test and it seems that the fields are set on the instance, and the methods on the prototype, which is ideal. But is this the expected and documented behavior? And is this safe for future versions?
I think it can work the way you are doing and that it's safe. However if I understand well you are proceeding data calculation/transformation directly in the view.
So I would advise that you remove this logic from the view and treat it in the model part of a mvc or mv*, in your backbone models, or in your flux store for example.
This way you won't be mixing data transformation logic and pure rendering.
I would say so, I have been using things like this for a while and have not seen any issues. For example, let's say you want a handler of some sort that you want to pass to nested components, you would create the function in this component and pass it as a prop to a child. I believe they have examples that use similar concept in the ReactJS Facebook site.
Under the hood React is just looping through the properties of the object you pass to createClass and copying them to the prototype of the Component. Primitive values like strings or numbers obviously cannot be copied by reference, so don't get shared across all instances, whereas objects, functions, arrays and so on will.
If you want to work with values that are just local to the component instance you need to use the state API. I'm not sure what you mean by "[state and props] do not affect rendering in any way directly, they just work together to control the rendering". The whole point of props and state is that they work together to generate values to be used when (re)rendering.
https://facebook.github.io/react/docs/component-api.html
A React component should only render in response to either changing props or changing state. You cannot/shouldn't trigger a re-render by mutating other fields directly.
You need to think of your component as something closer to a pure function. State and props go in at the top, and static VDOM/HTML comes out.
I would re-write your example as,
var Component = React.createClass({
getInitialState: function () {
return {field: 'value'};
},
render: function () {
var field = this.state.field;
return (<div>{field}</div>);
},
method: function () {
var field = this.state.field;
// do something with field
this.setState({field: 'anotherValue'});
}
})

Categories