Trouble understanding JSX spread operator [duplicate] - ecmascript-6

This question already has answers here:
What are these three dots in React doing?
(23 answers)
Closed 1 year ago.
Given this example code from the React docs:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
I did some looking into what ...props actually evaluates to, which is this:
React.__spread({}, props)
Which in turn evaluates to {foo: x, bar: y}.
But what I'm wondering is, why can't I just do this:
var component = <Component props />;
I don't see understand what the point of the spread operator is.

This helps make your code more succinct - since props is an object, the spread operator takes the properties of the object you pass in and applied them to the component. So the Component will have properties a foo with a value of x and a bar with a value of y.
It would be the same as:
var component = <Component foo={props.foo} bar={props.bar} />;
just shorter

One of the best overviews of how object-rest-spread syntax works with react is published at reactpatterns.com:
JSX spread attributes
Spread Attributes is a JSX feature. It's syntactic sugar for passing all of an object's properties as JSX attributes.
These two examples are equivalent.
// props written as attributes
<main className="main" role="main">{children}</main>
// props "spread" from object
<main {...{className: "main", role: "main", children}} />
Use this to forward props to underlying components.
const FancyDiv = props =>
<div className="fancy" {...props} />
Now, I can expect FancyDiv to add the attributes it's concerned with as well as those it's not.
<FancyDiv data-id="my-fancy-div">So Fancy</FancyDiv>
// output: <div className="fancy" data-id="my-fancy-div">So Fancy</div>
Keep in mind that order matters. If props.className is defined, it'll clobber the className defined by FancyDiv
<FancyDiv className="my-fancy-div" />
// output: <div className="my-fancy-div"></div>
We can make FancyDivs className always "win" by placing it after the spread props ({...props}).
// my `className` clobbers your `className`
const FancyDiv = props =>
<div {...props} className="fancy" />
You should handle these types of props gracefully. In this case, I'll merge the author's props.className with the className needed to style my component.
const FancyDiv = ({ className, ...props }) =>
<div
className={["fancy", className].join(' ')}
{...props}
/>
-- quoted from reactpatterns.com by #chantastic
Another good overview was published on the babeljs blog post React on ES6+ by Steven Luscher:
Destructuring & spread attributes
Often when composing components, we might want to pass down most of a parent component’s props to a child component, but not all of them. In combining ES6+ destructuring with JSX spread attributes, this becomes possible without ceremony:
class AutoloadingPostsGrid extends React.Component {
render() {
const {
className,
...others // contains all properties of this.props except for className
} = this.props;
return (
<div className={className}>
<PostsGrid {...others} />
<button onClick={this.handleLoadMoreClick}>Load more</button>
</div>
);
}
}
-- quoted from "BabelJS.org blog - React on ES6+" by Steven Luscher

Related

How to pass data with Html Attributes after mapped Array without using another React components?

I am trying to pass data with HTML attribute without using another component to handleClick but I couldn't handle can anyone help me please
const handleLiClickFirst = (airport) => {
setFirst(airport.target.innerHTML);
console.log(airport.target.lat); // I can't read the data here
};
<div className="header__first">
<TextField
id="outlined-basic"
label="From"
variant="outlined"
value={first}
onChange={(e) => setFirst(e.target.value.toLocaleLowerCase())}
/>
<ul>
{resultFirst.airports?.map((airport, i) => {
return (
<li
key={airport.iata}
airport={airport}
onClick={handleLiClickFirst}
lat={airport.latitude}
name={airport.name}
long={airport.longitude}
>
{airport.name} // I can read the data here
</li>
);
})}
</ul>
</div>
Random attributes like airport and lat aren't valid to attach to a native HTML element like <li>. However, you should be able to use data attributes instead to store data on an HTML element.
And you will likely need to use data-airport={JSON.stringify(airport)} instead of just passing the JS object. And if you can avoid passing the entire object in, (by saving each of the properties that you need separately, like you are already doing with latitude, for example) that may be best to avoid to prevent creating massive HTML attribute strings.
I solve this problem without attributes and the handleClick Method after I couldn't reach the data. I removed handleClick from onClick and added the code in onClick
onClick={() => {
setFirst(airport.name);
// setFirstLatlong([airport.latitude,airport.longitude]);
setFirstLatlong({
lat: airport.latitude,
long: airport.longitude,
});
}}
#Jacob K I will try to use your method on an upcoming project. Thank you

How can I get the height of an element after it has rendered?

I want to get the height after an element is rendered. The element is inside another component than which I want to detect it in.
I know I can do it like this:
const heightOfStepper = document.getElementsByClassName(
"step-indicator"
)?.[0]?.offsetHeight;
const minHeight = useMemo(
() => `calc(${heightOfStepper}px + var(--small-spacing))`,
[heightOfStepper]
);
But when I console.log out the heightOfStepper, the value is 0 the first render, and if a add some code to my IntelliJ and save, then I get the real height of the stepper, since then the stepper is already mounted.
How can I get the height of an element after it has rendered, and the component is inside another component? (So I can not just use useEffect(() => ..., []))
In react you should avoid regular DOM queries (like getElementsByClassName) - use ref instead. What do you mean you cannot use useEffect? You can use it even in deeply nested component. If you want to get height of parent element you will want to use forward ref, e.g. like this:
// parent component
const App = () => {
const ref = useRef(null);
return (
<div ref={ref}>
MyDiv
<Child ref={ref} />
</div>
);
};
// child component
const Child = React.forwardRef((props, ref) => {
useEffect(() => {
console.log(ref.current.offsetHeight);
}, []);
return <p>I'm a child component</p>;
});
If your find yourself in need of passing ref deep through components tree or e.g. between siblings - pass ref through context - that was answered here

How to render an element in React which was created by createElement()?

I have created an image element by using:
const image = document.createElement('image'); // or new Image();
How can I render this image variable in React ?
I don't want to use Html tags to do something like this:
<img src={image.src} ... />
Is there any other way ?
Well either create a <div class="parent"> </div> and then use
document.querySelector(".parent").appendChild(imageElement)
or simply,
document.appendChild(imageElement)
This is the wrong way to go about doing this. You shouldn't directly manipulate the DOM with React. I would instead have an array of objects in your state, and in your component, map the objects to the elements of your choosing. Like this
const Component = () => {
const [components, setComponents] = useState([{src:'path/to/src', alt:'altTag'}])
return(
<>
{
components.map(e => {
return(<img src={require(e.src)} alt={e.alt} />)
})
}
</>
)
}
Wrote this from memory/without testing so there might be something wrong so dont kill me. But if you need to render it anywhere, make it its own component. If it's truly just one image, then you don't need the array/map just use an object and render it same way

Passing css classes through props to a children component

So, i have a css file with determined classes, lets say (class1,class2,class3,etc) and i need to make a presentational component that passes through props their properties to a child component. if the called name in the child component then the properties of the css class are available to use.
props.children = the component(s) nested inside that component. We can use React.cloneElement() in order to pass properties into the child.
const PresentationalComponent = ({ children }) => {
const hardCodedClassName = '.some-class';
return (
<>
{React.cloneElement(props.children, { className: hardCodedClassName })}
</>
)
}
// Usage
<PresentationalComponent>
<ChildComponent />
</PresentationalComponent>

Understand es6 and jsx in react

I am going through the official redux tutorial.
http://redux.js.org/docs/basics/UsageWithReact.html
In the file
components/Link.js have the following code
import React, { PropTypes } from 'react'
const Link = ({ active, children, onClick }) => {
if (active) {
return <span>{children}</span>
}
return (
<a href="#"
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
}
Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}
export default Link
What I am wondering why function link accepting the variable surrounded by curly braces. Secondly, the return statement inside the if block has the span jsx tag without the braces but the secondly the return statement outside the if block with the <a> tag has a braces. Can anyone explains why?
EDIT: After finding out about destructuring assignment from the answers I read the following article about how to use it in a function and it became very clear to me.
https://simonsmith.io/destructuring-objects-as-function-parameters-in-es6/
That's a stateless function, you can define react classes as plain JS functions when they don't have state and life-cycle methods
The curly braces are placed in there to use an amazing es6 feature called Destructuring.
Basically using es6 that's the same as doing:
const Link = (props) => {
const { active, children, onClick } = props;
...
And without using ES6 it would be the same as doing:
const Link = (props) => {
const active = props.active;
const children = props.children;
const onClick = props.onClick;
....
About the return you use brackets when your jsx elements have more then 1 line.
The function argument uses destructuring assignment to extract values from an object.
The braces around JSX are here to keep the indentation clean.
You can do this:
return <div>
lol
</div>
But you can't do this:
return
<div>
lol
</div>
So, to keep the indentation of JSX clean, you have to wrap the markup with braces.