How to let react only render part of the component? - reactjs

I am writing a react app with css grids. I'm not going to include the css here but it is a 2x2 grid.
import { useState } from 'react';
function Container() {
const [count, setCount] = useState(0);
return (
<div className = "gridwrapper">
<div className = "top_left"> <SomeCustomComponent></div>
<div className = "bottom_left"> <CustomCounter counter = {count}></div>
<div className = "bottom_right"> <CustomCounter counter = {count+2}></div>
<div className = "top_right"><button onClick={() => setCount(count + 1)}>Click me</button><div>
</div>
);
}
function CustomCounter({count}){
return(<p>The count is {count}</p>)
}
I have two issues right now
Since setState would cause re-render, now it would re-render the whole thing. But I only need the bottom two cells to re-render since other parts of my Container component do not even depend on props.
In order for my grid structure to work properly, I need to wrap them in divs, why is that? I tried to assign classnames directly before but it didn't work

Regarding unnecessary re-renders - don't worry about it. The React diffing engine is smart enough to know which elements have actually changed and which ones haven't, and it will only update what is necessary.

You could use a useEffect listener to only change when specific variables change. As far as I know you can not just re-render a part of the component.
When you make a custom component, you need to make the className prop available on the child component aswell.
e.g.
function SomeCustomComponent(props) {
return (
<div className={props.className}>
// ... your component stuff here ..
</div>
)
}

Related

How to get DOM elements within (any/all) nested components in React?

I have a wrapper component in my App as follows. I have logic in the wrapper component that gets the DOM nodes with props.children that have a specific data attribute.
When the DOM elements are direct children, this works fine. When they are children of any nested component they're not found. How can I iterate through the entire structure and get all instances of the DOM nodes by attr?
I'm new to react and sure this should be straightforward, however I've not been able to implement from a number of examples / SO answers. I've tried to implement useRef and useContext hooks but can't get this?
// App.js
<Wrapper>
<div data-elem></div> // Get's found
<Component {...pageProps} />
</Wrapper>
// index.js
const Page = () => {
return (
<>
<div data-elem></div> // Not found
</>
);
}
// Wrapper.js ( simplified version )
export default function Wrapper(props) {
const detectedElements = props.children.filter((item) => item.props['data-elem'] === true);
console.log(detectedElements.length)
return (
<div className="wrapper">
{children}
</div>
)
}
Ultimately I managed to achieve this simply by using querySelectorAll within the useEffect hook. I'm not sure if this is the 'correct' way to do it, but is definitely the most straightforward of the the variations I tried, and seems to do the job, regardless of where the corresponding elements are.
useEffect(() => {
const detectedElements = document.querySelectorAll('[data-elem]')
}, []);

Why do (some) CSS styles break when I defined a React functional component inside another functional component?

Why does defining a React functional component inside another functional component break CSS transitions?
function Doohick({isOpen}: {isOpen: boolean}) {
const style = {
transition: 'opacity 2s ease',
...(isOpen ? {opacity: 1} : {opacity: 0})
}
return (
<div style={style}>
Doohick!!!
</div>
)
}
function Parent() {
const [open, isOpen] = useState(false)
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Doohick</button>
<Doohick isOpen={isOpen} />
</>
)
}
If I define Doohick outside of Parent, as above, everything works great. If I move the definition inside Parent, with no other changes, my CSS transitions break. Other CSS properties are fine.
Why does defining a functional component inside another functional component break CSS transitions?
Complicated Explanation of Why I Want To Do This
I hear you asking: why would I want to do that? I'll tell you, but bear in mind you don't need to know any of this to understand the specific problem.
I want to encapsulate the Doohick state in a custom hook:
function useDoohick() {
const [isOpen, setIsOpen] = useState(false)
const ToggleButton =
<Button onClick={() => setIsOpen(!isOpen)}>Toggle Doohick</Button>
const Doohick = <MyDoohick show={isOpen}/>
return {ToggleButton, Doohick}
}
function Parent() {
const {Doohick, ToggleButton} = useDoohick()
return (
<>
{ToggleButton}
{Doohick}
</>
)
}
But I also want the Parent to be able to pass its own props into Doohick or ToggleButton. I can almost achieve that that like this:
function useDoohick() {
const [isOpen, setIsOpen] = useState(false)
const ToggleButton = ({text}) =>
<Button
onClick={() => setIsOpen(!isOpen)}
>
{text}
</Button>
const Doohick = () =>
<MyDoohick show={isOpen} />
return {ToggleButton, Doohick}
}
function Parent() {
return (
<>
<ToggleButton text='Burninate' />
<Doohick />
</>
)
}
This works as advertised: ToggleButton renders with the expected label and controls whether or not Doohick is shown. But this pattern breaks some CSS styles (specifically, transitions) I have defined on Doohick. Other styles are fine.
I can still call it like this:
function Parent() {
return (
<>
{ToggleButton({text: 'Burninate'})}
{Doohick()}
</>
)
}
...and the transitions work correctly. But I would much prefer the standard JSX syntax here:
<ToggleButton text='Burninate />
Clearly, <Doohick /> and Doohick() are different. But what is it about the former that breaks CSS transitions here?
The root of the problem boils down to defining the custom components inside the Parent. The hook itself is irrelevant. But this pattern of encapsulating state in a custom hook while returning a customizable component is really powerful and almost works, so I'm hoping there's a way it can be saved.
TL;DR
Why does defining a component within another component break my CSS transitions (and possibly other styles I haven't found yet)? How can I get around this while still calling my nested component with JSX-style syntax?
Defining a component inside another component will always result in issues like this. Every time the outer component renders, you create a brand new definition of the inner component. It may have the same text as the one from the previous render, but it's a different function in memory, so as far as react can tell it's a different type of component.
The component type is the main thing that react looks for when reconciling changes. Since the type changed, react is forced to unmount the old component and then mount the new one. So rather than having a <div> on the page who's style is changing, you have a div with some style, then it gets deleted and an unrelated div gets put onto the page. It may have a different style, but since this is a brand new div, the transition property won't do anything.

use react component instead of motion's elements in Framer motion

Hi I have a component like this :
const Comp = (props) => {
return(
<div>
data here
</div>
)
}
and I want to use framer motion like this:
const Container = () => {
return(
<motion.Comp variants={variants} someProp="someProp"/>
)
}
The reason I want to do this is that I don't think making a lot of div as a wrapper is a great idea.
I could use motion.div in "Comp" component but some of my app component is made that I don't need animating all of them in my whole app. I just want a solution to add animations to the first element in the component.
Also I searched and I found a solution "motion(Comp)", but it's not working.
From the docs (https://www.framer.com/docs/component/#custom-components), you can wrap any component with motion() to use it as if it were a framer-motion component. You mentioned that you found that solution but that it's not working, and without any more details about it I can't give you a specific reason why. But most likely you're forgetting to pass the ref through to your component, like this:
const Foo = React.forwardRef((props, ref) => <div ref={ref} />)
The ref is the way that framer-motion will identify which element on the page is the one it should be animating, so it needs to be passed to the correct element. Once that's done, you can wrap it with motion:
const MotionFoo = motion(Foo)
Keep in mind that regardless of how many elements your custom component has, only the one that has the ref passed to it will be animated. For example:
const BusyFoo = React.forwardRef((props, ref) => (
<div>
<div ref={ref}>I will be animated!</div>
<div>I won't be animated. :(</div>
</div>
)

React - Calling the function of other component

I'm very very new with React. Actually I started to learn today.
I want to build a single sidebar component in a website.
I toggle a sidebar using UseState and a function called openHandler that verifies of the sidebar is already active.
I'm wondering how should I approach the toggle inside of the sidebar. How should I change the state of the sidebar if all the handle is done in the main file (App.js)
I'm really sorry if this question don't make sense.
Here is a SandBox example of what I'm talking about.
https://codesandbox.io/s/hopeful-framework-c4k1h
If someone know what should I learn to play with that would be great.
Thanks in advance
you can pass the main handler to sidebar via props and bind it to insider toggle.
appjs
...
<CartBox openHandler= {openHandler} className={ToggleCartState} />
...
cartBox.js
...
<Toggle click={props.openHandler} />
...
Nice to read https://reactjs.org/docs/lifting-state-up.html
I assume you want to be able to change the state of the sidebar by clicking a button from another sibling / child of a sibling component.
if that's the case you'll need to put the useState hook in the higher level parent, then pass the state / an it's setter method as a prop to the children that will use it.
here is an example of what I mean.
Parent Component
function parent() {
// the sidebar state
const [sidebar, setSidebar] = useState(false);
// helper function that toggles state
function toggle() {
setSidebar(!sidebar);
}
return (
<section className="Parent">
{ /* Conditional Render */
sidebar ?
<Navbar stateManager={{toggle}} />
: <HamburgerIcon stateManager={{toggle}} />
}
</section>
)
Navbar / HumburgerIcon
function Navbar({stateManager}) {
// you now passed state and it's set method to the child
const {toggle} = stateManager;
return (
<div onClick={toggle}>
component content
</div>
}
You can put them all in same file and still do the same thing.

Improve the performance of Reactjs for rendering large amount of components

I am developing a gallery and I have a performance problem when I exceed 200 components.
Currently in my gallery I can filter images and sort them according to certain attributes.
I tried to use the "Lazy Load" to improve the performance but it brings me other problems that I did not have before. Moreover it does not solve the problem if I still display all my elements.
I would like to try a thing a bit like "react-virtualized" which updates the DOM permanently but I do not really know how to do it and I have the impression that "react-virtualized" was not thought for operate with entire components.
My Gallery component looks like that:
const Gallery = memo(props => {
const { imgs } = props;
const { onClickHere } = props;
return (
<div className="cards-container">
{imgs.map((img, index) => (
<GalleryImage
img={img}
index={index}
onClickHere={onClickHere}
/>
))}
</div>
);
});
My Image component looks like that:
const Image = props => {
const { img } = props;
const { index } = props;
const { onClickHere } = props;
const { alt } = props;
return (
<div className="cards-child">
<div className="gallery-card">
<img className="gallery-thumbnail" src={img.uri} alt={alt} />
<div className="card-info">
<span className="card-info_text">
<span className="badge badge-secondary">
{img.attr.join(", )}
</span>
</span>
</div>
<span className="card-icon-open fa fa-expand" onClick={e => {onClickHere(e, img, index);}} />
</div>
</div>
);
};
These components display a clickable image list that is all the same size.
Do you know if I can actually use react-virtualized and did it badly (if that's the case can you help me to see more clearly) or do you know a way to improve these performances ?
On one hand we have the render cycle from the Virtual DOM. In this case React will render every component when its props change or when its parent renders.
To improve performance here you could memoize the component so that it will only render if its props change, and not when the parent changes. So in your case, you probably want to memoize Image rather than Gallery. Note that Gallery is receiving onClickHere and passing it to Image. Make sure the callback is wrapped in a useCallback so a new reference is not created on every render (which memo would recognize as a new parameter and render the component).
On the other hand we have the actual DOM rendering. In this case React will only render anything if there is a change, and only change the necessary HTML.
Virtualization plugins help to avoid rendering all the HTML, and only redering the necessary. This is helpful so the browser has the minimum necessary HTML, because if the amount of nodes is large it might be laggy. Before implementing these try to reduce the amount of divs inside Image to a minimum.
Only apply performance improvements when necessary and test to see if you are getting benefits from them or not. In your case the main issue might be displaying too many HTML elements. Also it seems like your map function is missing a key.

Resources