ReactJS - Different Component vs Conditional Rendering - reactjs

Let's say I'm building out a <TextInput /> component.
I have 3 variations. In variation #1, the label is above the textinput; in #2 the label is the placeholder and moves up when clicked; in #3 it's inside the input above the placeholder.
Question1: Performance wise, which method of having the 3 variations is faster in terms of load times in the UI - putting it all in one component with conditional rendering in the JSX everywhere and having a prop to toggle between the three variations, or having 3 totally separate components and then importing a TextInput object and calling a specific variation in this fashion <TextInput.VariationA />?
Question2: Does importing the full TextInput object somehow lag the component instead of just importing VariationA by itself? Say for example if there are 1000 components inside the main import (like when importing Icons).
// This TextInput has TextInput.VariationA
// TextInput.VariationB and TextInput.VariationC
import TextInput from "myCustomTextInput"
// vs
import { VariationA } from "myCustomTextInput"

I don't think performance is going to problem, but I suppose you would handle it three different class with one component. You can give classnames via props where form component is used. And three different css styling will probably works.
I haven't try it before but I think it might works.

Related

Reusable components structure (react native)

I want to seek advice regarding reusable components structure in react-native. I wanted to make them lean and adaptive. What I thought was to have generic components as wrappers and then have specific components using those wrappers. e.g. For products carousel I pass products data to Carousel component (just a FlatList) that renders Card component multiple times that has products details and product related icons. But what If I want to have categories or anything else inside card?
What I thought is to make card content passed as props
<Carousel>
{.... // ProductContent}
</Carousel>
<Carousel>
{.... // CategoriesContent}
</Carousel>
But it seems like I am over complicating things as I'll pass data to carousel, then carousel will pass it to card then card will pass it back to my content and carousel is mere a Flatlist and card is mere a TouchableOpacity. And also it will not look clean as I will have to define the content wherever I am using the Carousel. Why not just create two separate carousel components
<ProductCarousel />
<CategoriesCarousel />
Similarly I have a <PopUpModal /> component. which I am using for showing product details. Should I pass product content as children to keep content generic or just create <ProductDetailModal /> as a component and create more modals if required
So the point is whether to have specific bits and pieces of the app as components so that connecting them will complete the puzzle or to have generic customizable wrappers like components. Or something in between
I recommend atomic design.
Its hard to explain it here, so Ill leave a link.
https://bradfrost.com/blog/post/atomic-web-design/
The key point is to break(modularize) everything into tiny, replacable & reusable bits, and actually reusing and replacing them.
Another important, yet often neglected point is that separating smart and dumb components.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
This is surely neglected and also seems cumbersome to aplly, but will simplify the codebase by detaching view related logic to data(api) related logic.
So those are the two rules I try to stick to.
P.S. Just as im_baby has pointed out in the comments, there is no answer, and we all compromise at some point. So try to be long sighted and practical, dont be dogmastic to rules, neither be short sighted and mess up the overall code quality and structure for immediate comfort
You will make a component like this giving the parent component all the liberty to change it through props.
render() {
const { all the props that you want to give your component} = this.props;
return (
<Carousel>
{this.props.childern} // like this you can design your one carosal and cahnge the data/view in the carosal.
</Carousel>
);
}
Then in your parent component you need to import this component like this:
import CarouselComponent from '../CarouselComponent'; //path to your component
<Carousel>
<View>
//any view you want to be rendered in the modal
</View>
</Carousel>

Ag grid using framework cell renderer keeps re-rendering on any store change

Trying to implement a custom cell framework renderer with React and it seems simple enough. Create the React component, register it with frameworkComponents.
The data that populates rowData is coming from my redux store. The custom cell renderer is a functional react component.
The issue is that because I'm using a frameworkComponent - a React component in my case - as a cellRenderer, it seems that any change in the data for the grid the I'm getting via useSelector(selectorForMyData) causes a re-render of my frameworkComponent, which on the browser, looks like a random, annoying flicker. The application is heavily wired into redux
Two questions:
1 - How come when I natively use ag grid to render this cell using a AgGridColumn without any custom cell renderers, it doesn't cause this re-rendering behavior on the same store changes? I have a click event bound to the main page that toggles a flag to false (in the case a snackbar alert was open).
2 - Is there any way to get around this? I've tried wrapping my return statement in the framework cell renderer component with a useMemo with the params as a dependency, but that doesn't seem to work. Also tried making a render function via useCallback with the same idea as useMemo and that doesn't help either :/
Thanks
pseudo-code for situation:
App.tsx:
<MyAgGrid/>
MyAgrid.tsx:
const MyAgGrid = () => {
const data = useSelector(selectorForData);
return (
<AgGridReact
rowData={data}
frameworkComponents={
{'myCustomRenderer': CustomRendererComponent}
}
columnDefs={
['field': 'aField', cellRenderer: 'myCustomRenderer']
} />
);
};
CustomCellRendererComponent.tsx:
const CustomCellRendererComponent = (params) => {
console.log("params", params) //logs on every redux store update
return (
<div>HELLO WORLD</div>
);
};
The cells that are rendered via the CustomCellRendererComponent are re-rendered on any redux store change. I'm guessing it's due to useSelector causing the re-render on store changes, which is expected, but then it doesn't answer my first question.
EDIT:
I went "function as class" route shown here ("MyCellRenderer") and so far am not seeing the re-rendering issue, so I will stick with that for now even though it's god ugly. This leads me to believe my issue is trying to fit a React component/hooks, with its lifecycle nuances, as a cell renderer is causing problems. Still, I feel like there should be a way to prevent the behavior of constant re-rendering, otherwise it's a pretty useless feature
EDIT pt 2:
I dug deeper and while I haven't found an out of the box solution for it, I added the reselect library to memoize some of my selectors. The selector I use to get rowData is now memoized, and I'm no longer seeing this issue. Will mark as answer in a few days if no one provides a better, ideally out of the box (with redux or ag grid), solution for it.
As I stated in one of my edits. I figured it out, kind of.
I added the library reselect to the application, and it fixes the symptoms and I'm content with it going forward. It allows you to memoize your selectors, so that it only registers changes/"fires" (leading to a re-render) only if the any of the dependency selectors you hook it into changes/fires, so now, my grid doesn't "flicker" until the actual row data changes since my selectorForRowData is memoized!
I'm still uncertain why prior to using a frameworkComponent (React Component) as a cell renderer I wasn't seeing the symptoms, but I'm happy to just assume Ag-Grid has a lot of performance tricks that clients may lose when plugging in their own custom functionality, as is the case in a custom cell renderer.

Any cost to using `makeStyles` in a component that will be iterated (like a list item)?

I've got a component that's only ever going to be rendered as part of a list. Let's call it MyListItem. The easiest thing from a coding perspective is to localize the const useStyles = makeStyles(...) right inside MyListItem. But in that case, if there are 100 list items, then I'm potentially invoking useStyles 100 times.
Normally, I'd just say screw it and put the useStyles in the parent (e.g. MyList) and then pass classes down to MyListItem. That way, useStyles is only computed once, rather than 100 times.
However, MyListItem could get invoked by more than 1 parent (e.g. MyListOne, MyListTwo). That means that any parent that wants to invoke MyListItem needs to contain the styles for MyListItem, which is less than ideal.
So here's the question: is Material-UI smart at all about detecting that the same useStyles is being invoked again and again? Will it really write out 100 versions of those styles, or will it collapse all those down at runtime? My guess is the answer is "no", but I figured it was worth asking.
Thanks!
Material-UI is definitely smart in this regard. If it wasn't, you would have the same problem with Material-UI's components such as ListItem. So long as makeStyles is being called at the top-level (which is the typical pattern) such that you are always using the exact same useStyles function, you are fine. A component that uses styles has more overhead than one that doesn't, but it won't duplicate the styles in the DOM.
The smarts in the library can be found here: https://github.com/mui-org/material-ui/blob/v4.9.13/packages/material-ui-styles/src/makeStyles/makeStyles.js#L56
let sheetManager = multiKeyStore.get(stylesOptions.sheetsManager, stylesCreator, theme);
This is the cache lookup for a particular style sheet. stylesOptions.sheetsManager will always be the same unless you are customizing it (very uncommon) for part of your component hierarchy using StylesProvider. stylesCreator is the argument to makeStyles -- i.e. the declaration of your styles. So for the same makeStyles call and the same theme, it will find the same styles and avoid re-generating them.
You can easily verify this is all working as intended by looking at the <style> elements in the <head> of your document via developer tools. The image below is from this page which is a sandbox for this other StackOverflow answer.
You can see in the image style elements for the different Material-UI components used in the page (e.g. MuiSvgIcon, MuiListItem, etc.). You can also see one generated from makeStyles and one generated from withStyles. The withStyles case is similar to what you are describing -- it is styles for a ListItem component that is used multiple times in the page, but the styles only occur once in the document.

Large react app with styled components performance problems

I'm a developer of a fairly large react application. A part of the application is a detail form that is loaded dynamically and can consist of about 100 input fields of different data types (different components).
The initial render of this form takes about 500ms on my laptop and i'd like to make it faster. I'm not quite sure what causes this render time but i share with you what i have:
This is a screenshot of the react profiler. As you can see there are some commits happening but one (orange) stands out. This is where the actual form is rendered. The form is a hierarchy of composed layouting boxes, other containers and the field container with the label and the data-type component inside. A single component does not take too long to render, but added up is why I end up with 500ms.
if we take a closer look at a small part of this screenshot above we can see this:
This is a small section inside a date edit field that gets rendered because we have a field of data type date. The first element is a styled component
const StyledDateAbstractWrapper = styled.div`
&& {
align-items: center;
cursor: ${props => props.immutable ? 'not-allowed' : 'default'};
display: flex;
input {
display: ${props => props.immutable ? 'none' : 'block'}
&:last-of-type {
display: ${props => props.immutable ? 'block' : 'none'}
}
}
}
`
as you see, nothing fancy. But it takes 2.3ms to render. And inside a styled button that takes 5 ms. Let's say i have a couple of these and that's how i end up with 500ms.
At this point i really think styled-components is the problem because i have some components take a couple of milliseconds as expected and many many small styled-component wrappers that each take more than a millisecond.
So my question... is there something i can further investigate? Or are there clear no-goes for styled components that are rendered many times such as element selectors?
Im using styled-components 5.0.1
Another approach would be using something like https://github.com/bvaughn/react-window but i dont have a list really. more some composed components.
Providing a running application is a bit tricky at the moment.
thank you for any suggestions
You are right, Styled Components is slowing down your application. It's easy to reason when you think about what each styled object does:
parses the injected props;
generates the actual CSS code;
generates an unique class identifier;
applies the unique identifier to the className of the underlying React object;
injects the generated CSS at the end of the <head>, under the unique class identifier.
Only then the browser renderer will apply the CSS onto the DOM elements.
Remember: this is done for each styled object you create. With many elements, it's easy to see how this blows up, because it doesn't scale up well.
My suggestion is to move away from Styled Components and adopt SCSS modules. You'll end up with a plain static CSS file, and unique class names generated at buildtime, and nothing done at runtime. Can't go faster than that.
Foo.module.scss
.myTitle {
font-size: 3em;
}
Foo.tsx
import css from './Foo.module.scss';
function Foo() {
return <div className={css.myTitle}>Hello</div>;
}
export default Foo;
The cons are that you'll need separate .scss files, and injected values will have to be reworked into class names with the SCSS preprocessor. But there are advantages too: you can have all built-in SCSS features, wich are great.
I can't say much without seeing the proper implementation on this case, but from what I can think of you have a few options.
1 - Instead of having components with display: none you can simply remove them from the DOM with { !immutable && <Component /> }, this way the component won't take space in the VirtualDOM
2 - Second problem could be the Form library you are using, here is a performance overview of some of them. Performance Comparison. Maybe changing the lib you are using also helps.
First and foremost: usability wise, 100 form elements is insane! No user would ever go through the hell of filling this many (or even a quarter) of these elements. I suggest going back to your designer/product people and telling them to come up with a better way to model the business flow.
Really, forget about performance. No user will ever fill this form and it is better to realize and fix it now than in hindsight.
As for performance: 100 form elements is a lot! A desktop browser can surely handle that HTML, but rendering it all in React has its overhead in JS/DOM land. I'd say that half a second is quite good, considering the amount of work the browser has to carry out. And with styled-components in the mix, you're also straining the CSSOM.
There are additional optimizations and techniques which can be applied, though they won't be necessary once you restructure the app to not render so many DOM elements which your users won't be able to handle anyway.

trying to rendering multiple components in standard DOM, using React

I'm trying to render multiple components on 'standard' DOM. The first
set(below) renders, but the second, identical set, does not. Any
idea why? btw, the 'Card' component is the same as 'JunkComponent'.
Could this be a "unique id" thing?
thanks in advance
this link should best explain:
http://pastebin.com/8f7HVyyj
You shouldn't create elements with same ids.
As you have two elements with id JunkTestComponent on your page. React renders <JunkComponent /> only in the first one.
Also if you want to render component in the #Card too you should call:
ReactDOM.render(<JunkComponent />, document.getElementById('Card'));

Resources