Protect against React Instances - reactjs

I'll keep it short. Using Typescript 2.7.2 and ""#types/react": "^16.0.40","
Having really bad problems trying to protect against React Component instances.
consider the component
IFooProps {
element: SomeType
}
...component<IFooProps> {
....
render() {
return (
react.cloneElement(element, {onClick: this.myOnClickHandler});
)
}
}
If element is a react component instance the onClick will be applied to the instance and not run ever. However if its a valid element type I.E div, input, span etc the onClick will get cloned onto the element and run fine.
what type do i replace "SomeType" with to allow JSX elements like div, span, input etc... but not Component instances.
<Foo element={<Bar/>} should fail typecheck
<Foo element{<div/>} should pass typecheck
iv'e tried ReactNode, ReactElement, ReactHTML, JSX.Element, etc etc etc nothing seems to properally type check it.

Related

"Introduce Prop" refactoring in WebStorm for React for functional components

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?

Converting HTMLElement to a React Element, and keeping any event listeners

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!

In Typescript, is there any way to typecheck passed-in JSX.Element's?

Consider this simple case:
Client of library:
class ClientComponent extends React.Component<any,any> {
render() {
const myNestedElement = (<LibNestedComponent/>)
return <LibComponent nested={myNestedElement}/>
}
}
Library:
class LibNestedComponent extends React.Component<any,any> {
render() { return <div>nested stuff</div> }
}
interface LibComponentProps { nested: JSX.Element }
class LibComponent extends React.Component<LibComponentProps,any> {
render() {
return <div>{this.props.nested}</div>
}
}
As the author of Lib, I'd like to be able to tell my LibComponent clients, via the LibComponentProps interface, that the passed-in nested prop must be an element of type LibNestedComponent - not just any old j.random element. But AFAICT, there's no way to do this; the Typescript doc even says:
The JSX result type. By default the result of a JSX expression is typed as any. You can customize the type by specifying the JSX.Element
interface. However, it is not possible to retrieve type information
about the element, attributes or children of the JSX from this
interface. It is a black box.
Does anyone have a workaround that achieves this kind of typechecking without too much pain?
(The example is deliberately trivial and is not meant to be a sensible use-case.)
Apart from my comment, I don't know if there is a typescript way to restrict the type of component that client component can pass. But there is a way through which you can determine the type of component that was passed as a prop.
You can check the name of the component which was passed and see which type is it.
if (this.props.nested.type.name === 'LibNestedComponent') {
console.log('A valid component is passed');
} else {
console.log('Invalid component is passed');
}
where nested is the component you passed in the example you provided.
In the below picture you can see the name of the component.
But again, this would be a run-time detection.
You can check the type of this.props.nested using the object
if (this.props.nested.type !== LibNestedComponent) {
throw new Error('Invalid prop passed. Make sure that an instance of LibNestedComponent is passed through the nested prop')
}
The problem with Hardik's answer is that, in case your code gets minified, the value of type.name will change and your code will fail. So you should go for the type property directly instead.

How do properly use Onsen's Navigator in React?

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.

Reconciliation in React detailed explanation

I am new to react JS. Can anyone explain reconciliation exactly how it works. I have tried understanding it from react official site but didn't got it.
This is how I understand :
You would agree that react makes thing simple and faster using components .
With JSX we can make things easier for user-defined components .
End of the day all of it gets translated to pure JavaScript (I assume you understand how React.createElement works)with function calls holding other function calls as its arguments/properties holding yet other function calls and so on ..
Anyway nothing for us to worry about as react does this on its own internally .
But how does this gives us an UI ?
Why it is faster from other UI libraries ?
<-- ALL HAIL ReactDOM library and the render method -->
An ordinary ReactDOM call looks like this :
// I have avoided the usage of JSX as its get transpiled anyway
ReactDOM.render(
React.createElement(App, { //if any props to pass or child }), // "creating" a component
document.getElementById('#root') // inserting it on a page
);
Heard about VirtualDOM ? { yes : 'Good'} : { no : 'still Good'} ;
The React.createElement construct element object with type and props based on the components we have written and place the child elements under a children key inside props.
It recursively does this and populates a final object which is ready to be converted to HTML equivalent and painted to the Browser.
This is what VirtualDOM is, which resides in reacts memory and react performs all its operation on this rather on actual Browser DOM .
It looks something like this:
{
type: 'div',// could be other html'span' or user-diff 'MyComponent'
props: {
className: 'cn',
//other props ...
children: [
'Content 1!', // could be a component itself
'Content 2!', // could be a component itself
'Content n!', // could be a component itself
]
}
}
After a Virtual DOM object is built, ReactDOM.render will transform it into a DOM node our browser can paint the UI according to those rules:
If a type attribute holds a string with a tag name—create a tag with all attributes listed under props.
If we have a function or a class under type—call it and repeat the process recursively on a result.
If there are any children under props—repeat the process for each child one by one and place results inside the parent’s DOM node.
The Browser paints it to the UI , this is an expensive task .
React is very smart to understand this.
Updating the component means creation of a new object and paint to UI. Even if a small change is involved it will make the whole DOM tree recreated .
So how do we make the Browser never have to create DOM each time rather paint only the necessary things.
This is where we need Reconciliation and the diffing algorithm of React ..
Thanks to react we don't have to do it our self manually , its taken care of internally here is a nice article to understand deeper
Now you can even refer the official React docs for Reconsiliation
Few points worth noting :
React implements a heuristic O(n) algorithm based on two assumptions:
1) Two elements of different types will produce different trees.
2) The developer can hint at which child elements may be stable across different renders with a key prop.
In practice, these assumptions are valid for almost all practical use cases.
If these are not met it will cause performance issues.
I am just copy Pasting few other points just to give a idea how its done :
Diffing :
When diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.
Scenario 1: type is a string, type stayed the same across calls, props did not change either.
// before update
{ type: 'div', props: { className: 'cn' , title : 'stuff'} }
// after update
{ type: 'div', props: { className: 'cn' , title : 'stuff'} }
That is the simplest case: DOM stays the same.
Scenario 2: type is still the same string, props are different.
// before update:
{ type: 'div', props: { className: 'cn' } }
// after update:
{ type: 'div', props: { className: 'cnn' } }
As type still represents an HTML element,React looks at the attributes of both, React knows how to change its properties through standard DOM API calls, without removing the underlying DOM node from a DOM tree.
React also knows to update only the properties that changed. For example:
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
When converting between these two elements, React knows to only modify the color style, not the fontWeight.
///////When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls componentWillReceiveProps() and componentWillUpdate() on the underlying instance.
Next, the render() method is called and the diff algorithm recurses on the previous result and the new result.
After handling the DOM node, React then recurses on the children.
Scenario 3: type has changed to a different String, or from String to a component.
// before update:
{ type: 'div', props: { className: 'cn' } }
// after update:
{ type: 'span', props: { className: 'cn' } }
As React now sees that the type is different, it would not even try to update our node: old element will be removed (unmounted) together with all its children.
It is important to remember that React uses === (triple equals) to compare type values, so they have to be the same instances of the same class or the same function.
Scenario 4: type is a component.
// before update:
{ type: Table, props: { rows: rows } }
// after update:
{ type: Table, props: { rows: rows } }
“But nothing had changed!”, you might say, and you will be wrong.
If type is a reference to a function or a class (that is, your regular React component), and we started tree reconciliation process, then React will always try to look inside the component to make sure that the values returned on render did not change (sort of a precaution against side-effects). Rinse and repeat for each component down the tree—yes, with complicated renders that might become expensive too!
To make sure such things come clean:
class App extends React.Component {
state = {
change: true
}
handleChange = (event) => {
this.setState({change: !this.state.change})
}
render() {
const { change } = this.state
return(
<div>
<div>
<button onClick={this.handleChange}>Change</button>
</div>
{
change ?
<div>
This is div cause it's true
<h2>This is a h2 element in the div</h2>
</div> :
<p>
This is a p element cause it's false
<br />
<span>This is another paragraph in the false paragraph</span>
</p>
}
</div>
)
}
}
Children =============================>
we also need to account for React’s behavior when an element has more than one child. Let’s say we have such an element:
// ...
props: {
children: [
{ type: 'div' },
{ type: 'span' },
{ type: 'br' }
]
},
// ...
And we want to shuffle those children around:
// ...
props: {
children: [
{ type: 'span' },
{ type: 'div' },
{ type: 'br' }
]
},
// ...
What happens then?
If, while “diffing”, React sees any array inside props.children, it starts comparing elements in it with the ones in the array it saw before by looking at them in order: index 0 will be compared to index 0, index 1 to index 1, etc.
For each pair, React will apply the set of rules described above.
React has a built-in way to solve this problem. If an element has a key property, elements will be compared by a value of a key, not by index. As long as keys are unique, React will move elements around without removing them from DOM tree and then putting them back (a process known in React as mounting/unmounting).
So Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
Because React relies on heuristics, if the assumptions behind them are not met, performance will suffer.
When state changes: =========================================>
Calling this.setState causes a re-render too, but not of the whole page, but only of a component itself and its children. Parents and siblings are spared. That is convenient when we have a large tree, and we want to redraw only a part of it.
Reconciliation in the context of React means to make React's virtual DOM tree consistent with the real DOM tree of your browser. This happens during (re-)rendering
The key point is that there is no guarantee that a specific element of React's virtual DOM refers to the same DOM node of your browser for its complete lifecycle. The reason for this is React's approach to update the DOM efficiently. You can use the special key property to solve this issue, if a component contains dynamic or stateful children.

Resources