Understand es6 and jsx in react - ecmascript-6

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.

Related

How to prevent passing className and style props to component in React

Hi I have a custom button component. This button component should accept all ButtonHTMLAttributes except for className and style to prevent devs from adding their own styles. I am using TypeScript with React. How can I achieve this? I tried using Omit but it's not working.
I think what you can do is you can overwrite the styles and classNames properties like:
function CustomButton = (props> => {
return (
<button {...props} className={""} style={{}}/>
)
}
I don't know how you annotated types. But you can try the rest parameter concept.
function Button({className, style, ...restProps}) {
// Use the restProps object
}

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

Custom Angular directive is not working with async value

We have a custom directive in our project which we use when we want trim the long text in some UI elements. In one case it fails to work. There are no errors, no warnings, it's just no there. Checking the code in the DevTools shows no signs of this directive triggering (no HTML changes, no CSS added). The directive looks like this:
ngAfterViewInit() {
let text = <string>this.elt.nativeElement.innerHTML.trim();
if (!text || text !== (<HTMLElement>this.elt.nativeElement).innerText) {
return;
}
const limit = this.value || DEFAULT_VISIBLE_ENDING_LENGTH; // default length = 4
if (text.length > limit && this.elt.nativeElement.scrollWidth > this.elt.nativeElement.clientWidth) {
const startText = text.substr(0, text.length - limit);
const endText = text.substr(-limit);
this.renderer.setProperty(
this.elt.nativeElement,
'innerHTML',
`<div class="part1"><span>${startText}</span></div><div class="part2"><span><span>${endText}</span></span></div>`
);
}
}
It fails to work when the text to display & trim is obtained from observable (store selector). It doesn't matter if I use Observable + async pipe or if I map the value to the component property in selector subscribe.
#Component({
...
changeDetection: ChangeDetectionStrategy.OnPush,
})
this.sampleInProgress$: Observable<string>;
this.sampleInProgress: string;
...
this.sampleInProgress$ = this.store.select(fromAutomation.getInfoPanelData).pipe(
map(({ sample, experiment }) => {
this.sampleInProgress = sample?.sampleName; // does not work either
this.experimentInProgress = experiment?.parameterSet;
return sample?.sampleName;
}),
);
And the HTML:
<span class="label" gs-ellipsis>{{ sampleInProgress$ | async }}</span>
<!-- In this case, subscribe is done in the component -->
<span class="label" gs-ellipsis>{{ sampleInProgress }}</span>
Sorry for the bit messy code, I just didn't wanted to post almost the same code twice. I'm either subscribing explicitly or assigning the observable using async with it. Not doing both at the same time. The other place in the code where we use this ellipsis (and where it works) also uses OnPush Detection strategy the but that the value is provided by #Input.
I have a feeling that it has something to do with the ngAfterViewInit() in the directive itself, but I'm not sure. Directives are not my strongest field.
Any idea what can be the cause and how to fix it?
your directive handling happends too early. I assume you can hack it a bit and render element just when its content is ok with the help of ngIf directive.
<span class="label" gs-ellipsis *ngIf="sampleInProgress$ | async as value">{{ value }}</span>

ReactJS adding onClick handler into element given an HTML string

I'm given an HTML string from an API:
<div><h1>something</h1><img src="something" /></div>
I would like to add an onClick handler onto the img tag. I thought about using regex replace, but it's highly advised against.
I'm new to React... how would you go about solving this problem?
Any links or pointing into the right direction would be highly appreciated!
EDIT
Is there a way to add a listener to all anchor tags in a react friendly way? I'm thinking then I can just check the anchor tag's children, and if there's an image element, then I can run my code block.
I think a more idiomatic React way of doing this would be to refactor this into a component, replete with it's own onClick handler, and then insert your image URL via props. Something like
class MyImg extends React.Component {
onClick() {
// foo
}
render() {
return (
<div onClick={this.onClick}>
<h1>{this.props.foo}</h1>
<img src={this.props.imgSrc} />
</div>
);
}
}
Can you update your API to return JSON instead of HTML? JSON is easier to manipulate and you can create react elements on the fly, for example, let's assume your API returns this:
{
component: 'div',
children: [
{ component: 'h1', text: 'Something here' },
{ component: 'img', src: 'something.jpg' },
],
}
If you have that kind of response is very easy to create React elements at run time, you can also add events when creating these components, for example:
class DynamicContent extends PureComponent {
onImageClick = () => {
// Do something here!
console.log('Testing click!');
}
render() {
const children = response.children;
return React.createElement(response.component, null,
React.createElement(children[0].component, null, children[0].text),
React.createElement(children[1].component, {
...children[1],
onClick: this.onImageClick, // <-- Adds the click event
}),
);
}
}
You might want to create an utility that dynamically walks through the response object and creates all the components that you need.

Trouble understanding JSX spread operator [duplicate]

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