What does <Component render={({ state }) => {}} /> do in React? - javascript

I am currently learning ReactJS, and i wanted to use fullPageJS. It's working correctly, but there is some of the syntax that i don't quite get.
The component:
function home() {
return (
<ReactFullpage
render={({ state, fullpageApi }) => {
return (
<ReactFullpage.Wrapper>
<div className="section">
<h1>Slide 1</h1>
</div>
<div className="section">
<h1>Slide 2</h1>
</div>
</ReactFullpage.Wrapper>
);
}}
/>
)
}
export default home;
Now my question, what does render={({ state, fullpageApi }) => { return(); }} /> do? I can see that it is a property, but i don't really get what it's use is.

This is a pattern known as a Render Prop. It's a way to decide what to render via some two-way communication between your code and the code in ReactFullpage.
You pass a function in as one of the props. That alone a fairly common thing to do, such as passing in a function to an onClick prop. What's special here is how that function gets used.
While ReactFullpage is rendering, it will call your function. It's basically saying "hey, here's some data (the state and fullPageApi). What would you like me to render based on that data?" Your function then calculates and returns the elements you want it to render. And then ReactFullpage will use those elements in its final output.
For more information on render props, see react's documentation on the pattern

In react, render is a method that tell react what to display( what the DOM should look like) . Return in a method or function is the output of the method or function.

I'm also learning React! But since your question is just JS related, here's your answer.
First, let's put your snippet in pieces.
render={ ({ state, fullpageApi }) => { return (/** HTML */); } }
render= is XML, we con't care about it. It is HTML: you're passing a property to the Component element. The brackets {} indicate that it's JS in the HTML code: it means that you're giving JS values to the element's render property. The remaining JS code that was between the {} is:
({ state, fullpageApi }) => { return (/** HTML */); }
Which is a function! So the render prop takes a function that'll probably get executed later.
This anonymous function takes an object as a parameter, but it destructures it and only uses the state and fullpageAPI props, which makes it possible to use them as variables: instead of writing obj.state, you'd write state, as an example. You can read more about restructuring on the MDN docs. It then returns the XML in the parenthesis.

Related

Render only components with changes

I have an array with thousands of strings and is passed to a component:
Main component:
const array = ['name1', 'name2', 'name3'];
const [names, setNames] = useState(array);
const onClick = (index) => {
setNames(names.map((name, i) => {
if (i === index) {
return 'name changed';
}
};
};
return (
<ul>
{array.map((name, index) => (
<li key={index}>
<ShowName name={name} key={index} onClick={() => onClick(index)} />
</li>
)}
</ul>
);
ShowName component:
let a = 0;
export default function ShowName({ name, onClick }) {
a += 1;
console.log(a);
return (
<button type="button" onClick={onClick}>{name}</button>
);
}
There's also a button which changes a name randomly. But whenever the button is pressed, all the ShowName components are rerendering. I've been trying to use useCallback and useMemo, but the components are still rerendering x times (x is the length of the array).
const ShowNameHoc = ({ name }) => {
return <ShowName name={name} />
};
return (
<div>
{array.map((name, index) => <ShowNameHoc name={name} key={index} />)}
</div>
);
What should I do if I only want to rerender the component with a change?
You have a misunderstanding in the concepts here. The default is for React to call render on all children, regardless of whether the props changed or not.
After that happened, React will compare that new Virtual DOM to the current Virtual DOM and then only update those parts of the real DOM that changed.
That's why the code in a render method should be quick to execute.
This behavior can be changed by using features like useMemo, PureComponents or shouldComponentUpdate.
References:
https://reactjs.org/docs/rendering-elements.html (Bottom):
Even though we create an element describing the whole UI tree on every tick, only the text node whose contents have changed gets updated by React DOM.
https://reactjs.org/docs/optimizing-performance.html#avoid-reconciliation
Even though React only updates the changed DOM nodes, re-rendering still takes some time. In many cases it’s not a problem, but if the slowdown is noticeable, you can speed all of this up by overriding the lifecycle function shouldComponentUpdate, which is triggered before the re-rendering process starts.
...
In most cases, 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.
Also, read this for some more background info: https://dev.to/teo_garcia/understanding-rendering-in-react-i5i
Some more detail:
Rendering in the broader sense in React means this (simplified):
Update existing component instances with the new props where feasible (this is where the key for lists is important) or create a new instance.
Calling render / the function representing the component if shouldComponentUpdate returns true
Syncing the changes to the real DOM
This gives you these optimization possibilities:
Ensure you are reusing instances instead of creating new ones, e.g. by using a proper key when rendering lists. Why? New instances always result in the old DOM node to be removed from the real DOM and a new one to be added. Even when unchanged. Reusing an instance will only update the real DOM if necessary. Please note: This has no effect on whether or not render is being called on your component.
Make sure your render method doesn't do heavy lifting or if it does, memoize those results
Use PureComponents or shouldComponentUpdate to prevent the call to render altogether in scenarios where props didn't change
Answering your specific question:
To actually prevent your ShowName component from being rendered - into the Virtual DOM - if their props changed, you need to perform the following changes:
Use React.memo on your ShowName component:
function ShowName({ name, onClick }) {
return (
<button type="button" onClick={onClick}>{name}</button>
);
}
export default memo(ShowName);
Make sure the props are actually unchanged by not passing a new callback to onClick on each render of the parent. onClick={() => onClick(index)} creates a new anonymous function every time the parent is being rendered.
It's a bit tricky to prevent that, because you want to pass the index to this onClick function. A simple solution is to create another component that is passed the onClick with the parameter and the index and then internally uses useCallback to construct a stable callback. This only makes sense though, when rendering your ShowName is an expensive operation.
That is happening because you are not using the key prop on <ShowName/> component.
https://reactjs.org/docs/lists-and-keys.html
it could look something like this
return (
<div>
{array.map(name => <ShowName key={name} name={name} />)}
</div>
);

Returning a nested JSON object to front end as text in React

I'm successfully fetching from an API but having trouble rendering the data I want to the front end in React.
I'm trying to return the entire contents of the 'model' object within Article component which is a set of key/value pairs. The ArticleList component maps the body key representing an array and the type and model are passed as props to the Article component.
The JSON of the mock API being accessed is here for reference of the structure: https://www.mocky.io/v2/5c6574b33300009010b99de4
I can't use map on the inner objects because they are not arrays. The console.log in my code is correctly returning the contents of the model object for each entry in the array within the inspection window. However, I can't get it to display in the browser view.
In the Article component the use of Object.toString(model) is temporary code I added in to allow my browser window to render and it displays the following in the browser view:
Object: function Object() { [native code] }
So, to be clear my app is returning the full model object to the browser and not the contents within as desired.
ArticleList component is:
import Article from './Article';
const ArticleList = ({article}) => {
const articleNodes = article.body.map((section)=>{
return (
<>
<Article type={section.type} model={section.model} />
</>
)
})
return (
<>
{articleNodes}
</>
);
}
export default ArticleList;
Article component which is receiving the props of type and model is:
function Article({type, model}) {
console.log(model);
if (!type) return null;
return(
<>
<h1>type: {type}</h1>
<h1>Object: {Object.toString(model)}</h1>
</>
);
}
export default Article;
Please advise? I believe the fix could be simple within my <h1> tag of Article component but I have tried to no avail.
Edit: I want to be able to semantically tag each key value pair to render the images within the "url" key within img tags etc. Therefore I ideally need to have the ability to return individual JSX elements representing the properties in the object such as I have done with the map in ArticleList.
A component for every type
You'll need to create multiple components for each type of data you're receiving. That means a component for the heading type, for the paragraph type, etc.
const Paragraph = ({ text }) => (
<p>{text}</p>
);
export default Paragraph
Finding the right component for the right type.
Now that you've created multiple components for each type your API returns, you'll need to find a way to select the component that corresponds with the current type value.
An easy way to do this is to create a map object. The keys will represent the possible types and the values a function that return the component.
const typesMap = {
'paragraph': (props) => <Paragraph {...props}/>
}
The example above shows a single option called paragraph. (Other types heading, image, etc. are yours to add.) The value is a function with a single parameter called props. The function returns a Paragraph component and passes all available props to the component with the ... spread syntax.
The spread syntax allows us not to hardcode our props like the example below,
<Paragraph text={text} someprop={somevalue} />
but passes all the properties and values in the props object to the component, saving us effort and time.
Selecting the component
We have an object of keys that represent types and values that represent the components. All you have to do now is to select the component based on the type value. And we can do that like this:
const TypeComponent = typesMap[type];
Remember, the selected value is a function that returns a component, like <Paragraph/>. Which would be the same as something like this:
const ExampleComponent = ({ props }) => (
<Paragraph {...props}/>
);
In React (functional) components are nothing more than actual functions that return values. That's why we can write TypeComponent like a component and call it like this:
<TypeComponent {...props} />
The name TypeComponent can be anything you'd like. I thought that it was appropriate for the context it is in.
Now all you have to do is pass the model object to the dynamically created TypeComponent with the spread syntax, saving you the trouble of writing all the props and values for each type.
import Paragraph from './Paragraph';
const typesMap = {
'paragraph': props => <Paragraph {...props} />,
//'heading': props => ...
//'image': ...
};
function Article({ type, model }) {
if (!type) return null;
const TypeComponent = typesMap[type];
return <TypeComponent {...model} />;
}
export default Article;

Function inside component in reactjs

I have the following snippet of code
return (
<InputMask
mask="99:99"
*some other prop*
>
{(inputProps) => (
<Input
{...inputProps}
onSubmit={*Does something*}
/>
</InputMask>)
this is the return of a custom component, my question is regarding the syntax... how can a function be defined inside a component? Is this function being called whenever this component is rendered? What is passed into this function (in this case, how is InputProps passed in and what does it contain)?
As you can deduce, I am quite new to frontend development and react in general so any guidance or reference to any documentation will be of great help!
So firstly a quick definition. Here's some JSX where I'm using children:
<MyComponent>
<b>
This bold element
</b>
And this text are children of MyComponent.
</MyComponent>
Inside MyComponent is a regular render method, except importantly, it also has a props.children that it can do whatever it wants with:
render(){
return <div className="my-component">{this.props.children}</div>;
}
Importantly, children is just a prop like anything else. It can be anything you'd like it to be, including a function. This can be particularly powerful if a component wants to give some information to its unknown children. A nice example is some sort of iterator:
<Loop over={[1,2,3]}>
{item => <span>{item}</span>}
</Loop>
Where the Loop component is calling this.children, as it expects it to be a function:
render(){
// For each item in the 'over' prop, call this.props.children
return this.props.over.map(this.props.children);
}
Essentially this is what you have - in terms of when that function runs and what the inputProps object is, it's entirely up to that InputMask component, but it almost certainly will be running it when it renders.

Is it an anti-pattern to define a function component inside the render() function?

I want to know if it's an anti-pattern or if it affects the component somehow to do something like this:
render() {
const MyFuncComponent = ({ prop1, prop2 }) => (
// code here
)
return (
<div>
<MyFuncComponent prop1={something} prop2={else} />
</div>
)
}
Yes, this is an anti-pattern for the same reason we shouldn't use a Higher-Order-Component inside of render.
Don’t Use HOCs Inside the render Method
React’s diffing algorithm (called reconciliation) uses component identity to determine whether it should update the existing subtree or throw it away and mount a new one. If the component returned from render is identical (===) to the component from the previous render, React recursively updates the subtree by diffing it with the new one. If they’re not equal, the previous subtree is unmounted completely.
Normally, you shouldn’t need to think about this. But it matters for HOCs because it means you can’t apply a HOC to a component within the render method of a component:
render() {
// A new version of EnhancedComponent is created on every render
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// That causes the entire subtree to unmount/remount each time!
return <EnhancedComponent />;
}
The problem here isn’t just about performance — remounting a component causes the state of that component and all of its children to be lost.
This means that the new component will appear in the React tree (which can be explored with the react-devtools) but it won't retain any state and the lifecycle methods like componentDidMount, componentWillUnmount, useEffect will always get called each render cycle.
Solutions
Since there are probably reasons for dynamically creating a component, here are some common patterns that avoid the pitfalls.
Define the new component outside
Either in its own file, or directly above the parent component's definition. Pass any variable as props instead of using the parent component's scope to access the values.
const MyFuncComponent = ({ prop1, prop2 }) => <>{/* code here */}</>;
const MyComponent = props => (
<div>
{props.list.map(({ something, thing }) => (
<MyFuncComponent prop1={something} prop2={thing} />
))}
</div>
);
Helper function
A regular function that returns JSX can be defined and used directly inside another component. It won't appear as a new component inside React's tree, only its result will appear, as if it was inlined.
That way, we can also use variables from the enclosing scope (like props.itemClass in the following example) in addition to any other parameters.
const MyComponent = props => {
// Looks like a component, but only serves as a function.
const renderItem = ({ prop1, prop2 }) => (
<li className={props.itemClass}> {/* <-- param from enclosing scope */}
{prop1} {prop2} {/* other code */}
</li>
);
return <ul>{props.list.map(renderItem)}</ul>;
};
It could also be defined outside the component since it's really flexible.
const renderItem = (itemClass, { prop1, prop2 }) => (
<li className={itemClass}>
{prop1} {prop2} {/* other code */}
</li>
);
const MyComponent = props => (
<ul>
{props.list.map(item => renderItem(props.itemClass, item))}
</ul>
);
But at that point, we should just define a React component instead of faking it with a function. Use React in a predictable manner and to its full potential.
Inline the logic
It's really common to inline JSX inside of a condition or a map callback.
const MyComponent = (props) => (
<ul>
{props.list.map(({ something, thing }) => (
<li className={props.itemClass}>
{something} {thing} {/* other code */}
</li>
))}
</ul>
);
If we find ourselves copy-pasting this same inlined JSX everywhere, it might be time to wrap it up in its own reusable component.
I think in general people avoid defining functions in render but according to this blog post it is not neccesarily a bad practice. The blog post focuses on inline event handler functions being defined in render but I would guess it applies to any function defined in render. Defining functions in render means there is the overhead of redfining them each time render is called but that may not make a noticible performance difference depending on your component.
For the particular example you gave I would reccomend not to define another react component in render. If you do define any functions in render they should be cohesive to what render is doing. Defining another component or adding a bunch of functions inside of render can make in unwieldy and hard to understand what the code is doing.

Choosing the correct way to call function in stateful component

So i am having tough time figuring/understanding the correct way to call method inside a class in javascript for example
consider we have stateful component with various method like
addIngredientHandler = (type) => { //Adds one to the state of ingredient }
and
purchasingHandlerOpen = () => this.setState({purchasing: true}) //will show a order summary pop-up if we have more than one ingredient
We pass both of them to child component (using props) by calling them in a return of our stateful component like this
<BuildControls
ingredientAdded={this.addIngredientHandler}
purchasingHandlerOpen={this.purchasingHandlerOpen}
purchasableHandler={this.state.purchasable} />
and In our stateless child component we do
<BuildControl
ingredientAdded={() => props.ingredientAdded(el.type)}
/>))}
<button className={Classes.OrderButton} disabled={!props.purchasableHandler} onClick={props.purchasingHandlerOpen}>Order</button>
</div
Here we have use this at one place
ingredientAdded={() => props.ingredientAdded(el.type)}
and this in another
onClick={props.purchasingHandlerOpen}>
So my question is when do we call a method/function using {() => props.ingredientAdded(el.type)} and when do we use {props.purchasingHandlerOpen} and when do we probably do something like {props.purchasingHandlerOpen()}
Slight Note: In the above example where i do
<button className={Classes.OrderButton} disabled={!props.purchasableHandler} onClick={props.purchasingHandlerOpen}>Order</button>
If I do something like {props.purchasingHandlerOpen()} it throws infinite render error message, I I do something like {() => props.purchasingHandlerOpen} the button does not work.
First of all, you have to understand that the thing you're passing here are just functions, so there is nothing principally different in those 2 ways
There are few points you need to consider though:
First: since react.js uses shallow comparison, every time you're passing
ingredientAdded={() => props.ingredientAdded(el.type)}
you're actually pass function created just now, so it may cause unneeded calls of your children render function (you could easily avoid this by using shouldComponentUpdate though). This could lead to possible performance issues on big react trees so that you second approach is preferred.
Second: you could easily mix a some value via your first approach, something like
ingredientAdded={() => props.ingredientAdded(el.type, SOMETHING_FROM_STATE)}
Third. You can easily modify your event handlers and pass down them in react tree by generating functions which return functions:
class App extends React.Component {
generateFunction(something) {
return (arg) => {
this.props.myFunction(something, arg)
}
}
render() {
return (
<div>
<FirstComponent onClick={this.generateClickFunction('First')} />
<SecondComponent onClick={this.generateClickFunction('Second')} />
</div>
}
}
}
UPD
onClick should always receive function, not its results, like that:
<button ... onClick={props.purchasingHandlerOpen} />
if you are changing onClick to {props.purchasingHandlerOpen()} you are calling the function, so you're passing its result to props.
If you are changing onClick to {() => purchasingHandlerOpen} you are passing undefined (it's not a props.purchasingHandlerOpen, but purchasingHandlerOpen is undefined) so that React considers there is no a callback passed to the props
{() => props.ingredientAdded(el.type)} creates a new funciton which binds the el.type, see arrow functions
{props.purchasingHandlerOpen} does nothing since we do not execute the function (there are no (), call, apply). We simply pass the function reference.
{props.purchasingHandlerOpen()} runs the function.

Categories

Resources