React performance - DOM styling and component rendering - javascript

Can someone please explain what happens when I change the CSS class of a React component?
Let's say I have this code:
import React, {useState} from "react"
const Comp = props => {
const [class, setClass] = useState('visible')
if (props.hide) {
setClass('hidden');
}
return (<p className={class}>some text</p>)
}
My understanding of the process is this one:
Something in the parent component triggers a state change determining the Comp child to be called with property "hide" set to true
React will reconstruct the virtual DOM and the resulting html node of Comp will have the class set to "hidden"
React will update the DOM
Browser will render the DOM and apply the new styling
Am I correct?
Some related questions:
a. If the applied CSS class is just hiding the component, wouldn't be more efficient just to apply some conditional logic inside React parent component and don't display it at all?
b. If applying styling to html node just triggers the browser's painting method, why should we link CSS classes to the app state, thus triggering a full rendering? For me it seems inefficient.

Related

Why cant virtual dom prevent my child component rerendering?

I have a parent and child components, In my example, when increase button was clicked, child is gonna be rerendered.
import React, {useEffect,useState} from 'react';
export const App = (props) => {
const [count, setCount] = useState(0);
const handleCount = ()=>{
setCount(count+1);
}
useEffect(()=>{console.log("Parent was rerendered")})
return (
<div className='App'>
<h1>This is parent</h1>
<button onClick={handleCount}>increase</button>
<Child/>
</div>
);
}
const Child = props => {
useEffect(()=>{console.log('Child was rerendered')})
return (<>
<h1>This is child</h1>
</>)
}
I think it is pretty simply. And I also understand that, to prevent child rendering, I could us memo.
However, at some point, I studied the the concept of virtual dom. In it, it explained that the greatest of React is that it can prevent unchanged components to get rerendered, in order to prevent unnecessary computation.
If virual dom exist, isnt it that my child didnt change at all? Why is that my child get rerender eventually?
Edit:
I have to emphasize that my question is not about how to prevent it from re rendering. I knew the existence of memo. My question is virtual dom. React says virtual dom can compare the previous and current UI, then it will automatically prevent unchanged component from re rendering. If that's the case, why my example still got rendered? When virtual dom comes it, it doesnt make sense at all. Please dont neglect my concerns on virtual dom when providing answer
Changing parent state triggers re-rendering the component along with its children. Your child component will be re-rendered when you press 'increase' button without regard to its internal changes in view of the code you shared.
To prevent children from re-rendering, you may use React.memo like below
const Button = React.memo((props: PropsWithChildren<any>) => {
return (
<Button type="button" color={props.color} size={props.size} onClick={props.onClick}>
{props.label}
</Button>
);
});
If you do not use that kind of approach, children will be re-rendered by default.
If you wonder opposite; if state changes in child component, that will not cause parent component re-rendering. But there are ways to make that happen.
NOTE: DOM will not update unless something changed
Further reading
React Memo
UPDATE
I guess there's a misconception. When you press the 'increase` button, there will be re-rendering
The scenario:
When the state of a component changes, React updates the virtual DOM. React then compares the current version of the virtual DOM with the previous virtual DOM.
After comparing both and finding out which objects have changed, React updates them.
React updates DOM only if the render process produces changes. I hope it is now more clear to you.
To better understand, here's an article
React Virtual Dom
EXTRA UPDATE
Let's assume that two red nodes shown in the picture below are your components in the DOM (parent and child respectively)
When you press the 'increase' button in the parent component, it changes the inner state which leads to re-rendering of parent component along with its child component. So two red circles are re-rendered.
React, compares the old Virtual DOM and brand new Virtual DOM. Even if your parent and child component re-rendered, that will not lead to an update on the Real DOM because there's neither a visible change nor a structural change. You can think of it such as showing the current number on your parent component as a text. To visualize it, please take a look at the code below:
export const App = (props) => {
const [count, setCount] = useState(0);
const handleCount = () => setCount(count+1);
return (
<div className='App'>
<h3>Current count: {count}</h3>
<button onClick={handleCount}>increase</button>
<Child/>
</div>
);
}
Now, we have changed your parent component with an additional h3 tag in which we show the current count. In this case, if we press the 'increase' button that will lead to re-render again and this time the Real DOM will be updated because React will find out that there's a difference between old and new versions of the Virtual DOM.
So rendering and updating the real DOM are different processes.
Back to React.memo
If you use React.memo in your child component, React will render it, memoize the result instead of re-rendering and React will not perform a Virtual DOM difference check.

Using the same component in a child and parent in Vue

I have a VueJS project - which in one view there are parent and child components that are both using the same component called deleteModal.
When I trigger the modal from the child (to show it), it triggers both the child and parent modals (except no data is passed to the modal triggered by the parent). As I understand it, this is because I have used the component twice - in the parent and child - example below. To note, it works as expected from the paren
I've researched and tried a few things: setting a key value for each of the components, changing the components ref name among other things. I have also tried using v-show to only show component just before I trigger the parent model however, this solution is not ideal.
How can I only trigger the modal from the child?
Parent Component
<template>
<div>
<childCompt ref="childCompt" />
<deleteModal
ref="deleteModal"
#deleteTriggerAPI="deleteAPIParent"
/>
</div>
<template>
Child Component - childCompt
<template>
<div>
<deleteModal
ref="deleteModal"
#deleteTriggerAPI="deleteAPIChild"
/>
</div>
<template>
My old answear is not good at all. I personally to show and hide element using jquery in vue. For me right now this is best solution but maybe i don't know some best.
If you want use only vue i using also variable passing to child from parent which will support visable of your modal.
We pass variable with ":" and register event with "#".
<template>
<childComponent :isModalOpen="isModalOpen" #onModalClose="isModalOpen=false">
<template>
export default {
name:"parent",
data: () => {
isModalOpen: false
}
}
In child we catch this by using props. We need to define type of varialbe we pass. Different between props and data is that in props we cannot change value in child component.
export default {
name: "child",
props: {
isModalOpen: Boolean
}
}
And you can use this variable to show or hide modal. Also in child component we can create button to close modal and we emit event to parent in order to change variable value.
To do this we using this.$emit('eventName');
More information right here: https://forum.vuejs.org/t/passing-data-back-to-parent/1201
You could try globally defining the component,
ie, in main.js
Vue.component('deleteModal',deleteModal)

styled-components - create class method?

I need a way to create classes without styled nor css method.
Lets assume proper style system with components having only basic styles, but not positioning, margin etc.
e.g.
const Root = styled.div`/* Some styles */`
export const MyComponent = ({className}) => <Root className={className} />
So this is easy, I attach private styles for component and I allow it to receive and apply class from parent.
If I have "classic" css, I would use it like this
import styles from 'some-styles.module.css';
...
<MyComponent className={styles.someStyle} />
Which will work as expected.
Here is the problem:
How do I create a class with styled-components only? It was working with Glamorous once, but SC (and Emotion) is not returning class name from css.
I can't use separate css files just to allow this styling, which should be common use case (parent setting size of children)
Edit & solution in Emotion
I figured out that Emotion adds extra property css which is available for every jsx element.
It can be used to native <div>, styled <Div> or custom React component <MyComponent.
Babel is changing css attribute with css({...}) to className during compilation.
If <MyComponent> only accepts prop className it will receive it from parent via css prop.
I still belive I should be able to somehow pass styles from parent to child.
There is a way to do that, but no exactly the way you want to do.
In react js (you can't do this in React Native yet), you can pass the child component inside the parent style and it will add the style in the child when it's wrapped by the parent.
e.g.
const ParentComponent = styled.div`
${ChildComponent} {
/* Child css*/
}
`
Sorry if formatting is bad, I'm on mobile

Should you hide modals or remove them from the DOM in React?

I'm writing a React application with a lot of modals (but only one ever active at a time with no nested modals), and I'm unsure of which of these two solutions is preferable when it comes to handling showing and hiding these modals:
Have a state boolean variable in the parent component that determines whether or not the modal should be shown. If this boolean variable is false, don't render the component. If it's true, render it. Allow the modal to influence this event via a passed in props callback it can leverage. Example snippets:
{ this.state.prompt === "makePW" ?
<MakePassword closeModal={this.closePWModal} /> :
null
}
Now, within the component, it is always visible and does not control its lifecycle. If it is being rendered then it is visible.
Just always show the component in the parent component, like so:
<MakePassword />
Then within the component itself handle its entire lifecycle. That is, the component will have a boolean state variable for its visibility. Some snippets for this approach:
<Modal open={this.state.open} onClose={this.closeModal}>
<Modal.Header>Header</Modal.Header>
<Modal.Content>Body</Modal.Content>
</Modal>
There are also hybrid approaches, and other solutions as well I'm sure.
Regardless, I suppose the essence of my question is wondering about the preferable solution for showing and hiding modals: is it to always render them and just toggle their visibility, or to actually toggle between adding and removing them from the DOM? And, should the modal itself generally control its lifecycle or should the parent?
i prefer 1st one but if you want to use the second , i would extend the makepassword component from PureComponent to optimize your component.
Instead of writing shouldComponentUpdate() by hand, you can inherit from React.PureComponent. It is equivalent to implementing shouldComponentUpdate() with a shallow comparison of current and previous props and state. - source react docs
class MakePassword extends React.PureComponent{
...
}
I think it depends on your application.
For example, React-Bootstrap modals stay in the DOM and that allows for nice in/out animations.

React contenteditable in stateless component

I am trying to implement a contenteditable div inside a stateless react component.
I keep getting the below warning:
warning.js:36 Warning: A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.
How do I fix this?
Also how do I read contents of div on change?
Add suppressContentEditableWarning="true" to contenteditable div.
Reference: https://github.com/facebook/draft-js/issues/81
As with any React application, browser plugins and extensions that modify
the DOM can cause Draft editors to break.
Grammar checkers, for instance, may modify the DOM within
contentEditable elements, adding styles like underlines and
backgrounds. Since React cannot reconcile the DOM if the browser does
not match its expectations, the editor state may fail to remain in
sync with the DOM.
https://github.com/facebook/draft-js/issues/53
A known error. As for reading whats in a div, assign the element an id and..
oDoc = document.getElementById("divelement");
sDefTxt = oDoc.innerHTML;
Warning: A component is `contentEditable` and contains `children` managed by React
Resolved by adding...
//...
<div
suppressContentEditableWarning={true} // <-- Add this
className="MyClass"
onClick={ ()=> { onEidtHandler() } }
onBlur={ ()=> { onSaveHandler() }
>
Editable content
</div>
//...

Categories