We simply cannot write `<Foo style={ } ... />` in ReactJS? - reactjs

I got a little carried away and wrote
<Foo
style={ }
...
/>
in the old days, we actually had Foo as a <div> usually, so that could make sense, to actually impose that style on the <div>. But later on, we actually had <> ... </> and it'd be not good to have ReactJS impose an element (such as <div>) containing all those elements, so the style line doesn't make sense in this case.
So in a way, we never could treat a component as an element and give it some basic HTML attributes? How would we set HTML attribute for a component?

We could pass all HTML attributes to a React component as a prop and then inside the component's render function assign those props to the actual DOM HTML element.
const Foo = ({style}) => (
<div style={style}> // assigning inline style to HTML dom element
This is Foo
</div>
)
<Foo style={color: 'red'} /> // passing style as a prop to Foo react component
You can use JSX spread attributes and make it more flexible so you won't have to specify individual HTML attributes as a prop. You simply spread restProps over your HTML element.
const Foo({ name, ...restProps }) => {
return <div {...restProps}>{name}</div>;
}
Now you can pass DOM attributes to Foo and they'll be passed through to div.
<Foo
name="This is Foo"
className="foo-class"
id="my-foo"
style="color: red"
/>
When you use <> ... </> this is a shorthand of <React.Fragment>...</React.Fragment> which lets you only group multiple list of children WITHOUT adding extra node to the DOM when it gets rendered.
return (
<>
<Foo />
<Foo />
<Foo />
</>
);
This will render as
<div>This is Foo</div>
<div>This is Foo</div>
<div>This is Foo</div>
so if you find yourself need to pass a HTML attribute to <>...</> you can simply change react fragment to a <div>...</div> or <span>...</span> or other proper HTML elements and set your attribute on.

Related

React - is there a way to return JSX which contains braces? [duplicate]

I'm new to React and I'm trying to figure out the purpose/use of <MyComponent></MyComponent> vs <MyComponent />. I can't seem to find information on anything except self-closing tags.
I've created a basic tab scroller as a JSFiddle using the self-closing <MyComponent /> and subsequent props, and I'm wondering if there's a better way to write in React than what I've done.
class TabScroller extends React.Component {
render() {
return (
<div className="tabScroller">
<div className="NavList">
<TabNav handleClick={this.handleNavClick} />
<TabList
tabs={this.state.tabs}
activeTab={this.state.activeTab}
scrollPosition={this.state.scrollPosition}
handleClick={this.handleTabClick}
/>
</div>
<TabContent content={this.state.tabs[this.state.activeTab].content} />
</div>
);
}
}
// ========================================
ReactDOM.render(
<TabScroller />,
document.getElementById('root')
);
In React's JSX, you only need to write <MyComponent></MyComponent> when the component has child components, like this:
<MyComponent>
<Child />
<Child />
<Child />
</MyComponent>
If there is nothing between <MyComponent> and </MyComponent>, then you can write it either <MyComponent/> or <MyComponent></MyComponent> (but <MyComponent/> is generally preferred). Details in Introducing JSX.
Just as a side note, you'd access those children in your component via the special props.children property. More in JSX in Depth: Children in JSX.
Note that this is very much not like HTML or XHTML. It's its own (similar) thing with different rules. For instance, in HTML, <div/> is exactly the same thing as <div>: A start tag, for which you must eventually have an end tag. Not so JSX (or XHTML). The rules for HTML are that void elements (elements that never have markup content, such as br or img) can be written with or without / before > and they never get an ending tag, but non-void elements (like div) must always have an ending tag (</div>), they cannot be self-closing. In JSX (and XHTML), they can be.
The purpose of self-closing tags is simply the fact that it is more compact. This is especially useful when said component doesn't have any children that you typically wrap around a parent.
So usually for leaf components (i.e compoents that do not have any children), you use the self-closing syntax. Like: <Component />. And even if it has props, you can do: <Component foo="bar" />.
However, remember that children is a prop, so you could technically do:
<Component children={<span>foo</span>} />
but I find it less readable and advise against it (read disclaimer below).
To summarize, these are equivalent:
<Component /> = <Component></Component>
<Component foo="bar" /> = <Component foo="bar"></Component>
<Component children={<span>foo</span>}></Component> =
<Component><span>foo</span></Component>
You can use whichever approach you prefer. Though praxis is to use the short-hand version when there are no children.
Disclaimer: While defining childen prop by its object key value will technically work, doing so is strongly discouraged as it disrupts the API as it is meant to be used. Use this version only if confident in what you are doing.

test utility function which operatos over chidlren

I have a component which requires children (called layout component). There is also a function inside this component that iterates over children and does something. I want to test this function. So I've extracted this function to separate utility class and give children as parameter. It works well in browser.
But in test I have problem. I don't know how to define and pass children to this utility function.
const children = (
<div />
<div />
);
gives me compilation error: "JSX expressions must have one parent element.ts(2657)".
Surrounding divs with React.Fragment
const children = (
<>
<div />
<div />
</>
);
gives we me only 1 child in my layout component.
I'd like to pass children to the utility function but I don;t know how to define them.
Instead of :
const children = (
<div />
<div />
);
You can use a real array e.g.:
const children = ([
<div key="0"/>,
<div key="1"/>
]);
Or just use the fragment as you have already figured out.

Why return multiple elements in React is not allowed?

it can only return only one element tag in render.
In v16, we can render multiple elements by using an array.
so why cannot straightly write mutiple element tags in React?
render(){
return(
<div />
<div />
)
}
I mean that why cannot render multiple elements but not how to render mutiple elements.
React implementation relies on constructing a tree like structure which it uses to for reconcilation. When you return multiple elements from React elements from the render method, the assumtion that the tree will have one root node for the Component will not longer hold, hence making it difficult to process reconcilation algorithm.
Thus react gives you a limitation to provide a root node. If you return an array of elements from v16 onwards, react will internally create a dummy node as the parent.
From version 16.2 onwards React provides a React.Fragment component, that provides a cleaner syntax to return multiple elements
render(){
return(
<React.Fragment>
<div />
<div />
</React.Fragment>
)
}
React needs a parent element to render anything. You could put them in an array, or use a tool they gave for this exact purpose, the fragment component.
A fragment is just an empty node that won't show in the DOM allowing you to return multiple JSX components next to each other :
render(){
return(
<>
<div />
<div />
</>
)
}
If your linter is not a fan of this, you can use React.Fragment instead :
render(){
return(
<React.Fragment>
<div />
<div />
</React.Fragment>
)
}
The short answer to your question is... well this is just how React works and how their rendering engine is designed.
For now, multiple elements put together will not be interpreted as an array.
You can try
render(){
return[
<div> Div 1</div>,
<div> Div 2</div>,
<div> Div 3</div>,
<div> Div 4</div>
]
}

Can I pass HTML tag as prop - React

I want to do something like this.
In the parent component
<child tag={h1}/>
In the child component
<this.props.tag />
The problem "Unresolved variable or type div" throwing When i pass one of html components ( tags ) like ( div , h1, ext. )
UPDATED:
Yes, we can pass HTML tag as a prop. There are several ways based on what you want.
Passing tag as a prop
<ChildComponent tag="h1" />
And inside child component, we can use the tag as below.
const Child = ({ tag: Tag}) => (
<Tag>Hello World</Tag>
)
By setting dangerouslySetInnerHTML
<Child tags="<h1>Hello world</h1>" />
Inside child component:
const Child = props => <div dangerouslySetInnerHTML={{ __html: props.tags.outerHTML }}/>
Here is what you should know about dangerouslySetInnerHTML. In short, this exposes XSS attack.
This one is not related to passing as a prop. But you might wanna consider
If you are doing SEO task related (maybe nextjs) and you need to render conditional tag (sometimes h2 and sometimes h3). Then you can do as follow!
Conditional Statement
// Parent
const Parent = () => <Child isH3Tag />
// Child
const Child = ({ isH3Tag = false, children }) => isH3Tag ? <h3>{children}</h3> : <h2>{children}</h2>;
Here is a demo. https://codesandbox.io/s/8x65l707yj
JSX expressions must start with a cap, for example <Elment />
In your case, when you want to pass tag names, use createElement:
<div>
{React.createElement(this.props.tag, {children: <content>, prop1: <v1>, ...})}
<div>
Another alternative will be to use recompose#componentFromProp
You can pass tag as children, from parent Component like this:
In the parent component:
<Child> <h1></h1> </Child>
In the child component you can access like:
render() {
return (
{this.props.children}
)
}

When should I be using React.cloneElement vs this.props.children?

I am still a noob at React and in many examples on the internet, I see this variation in rendering child elements which I find confusing. Normally I see this:
class Users extends React.Component {
render() {
return (
<div>
<h2>Users</h2>
{this.props.children}
</div>
)
}
}
But then I see an example like this:
<ReactCSSTransitionGroup
component="div"
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
{React.cloneElement(this.props.children, {
key: this.props.location.pathname
})}
</ReactCSSTransitionGroup>
Now I understand the api but the docs don't exactly make clear when I should be using it.
So what does one do which the other can't? Could someone explain this to me with better examples?
props.children isn't the actual children; It is the descriptor of the children. So you don't have actually anything to change; you can't change any props, or edit any functionality; you can only read from it. If you need to make any modifications you have to create new elements using React.CloneElement.
https://egghead.io/lessons/react-use-react-cloneelement-to-extend-functionality-of-children-components
An example:
main render function of a component such as App.js:
render() {
return(
<Paragraph>
<Sentence>First</Sentence>
<Sentence>Second</Sentence>
<Sentence>Third</Sentence>
</Paragraph>
)
}
now let's say you need to add an onClick to each child of Paragraph; so in your Paragraph.js you can do:
render() {
return (
<div>
{React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
onClick: this.props.onClick })
})}
</div>
)
}
then simply you can do this:
render() {
return(
<Paragraph onClick={this.onClick}>
<Sentence>First</Sentence>
<Sentence>Second</Sentence>
<Sentence>Third</Sentence>
</Paragraph>
)
}
Note: the React.Children.map function will only see the top level elements, it does not see any of the things that those elements render; meaning that you are providing the direct props to children (here the <Sentence /> elements). If you need the props to be passed down further, let's say you will have a <div></div> inside one of the <Sentence /> elements that wants to use the onClick prop then in that case you can use the Context API to do it. Make the Paragraph the provider and the Sentence elements as consumer.
Edit:
Look at Vennesa's answer instead, which is a better explanation.
Original:
First of all, the React.cloneElement example only works if your child is a single React element.
For almost everything {this.props.children} is the one you want.
Cloning is useful in some more advanced scenarios, where a parent sends in an element and the child component needs to change some props on that element or add things like ref for accessing the actual DOM element.
In the example above, the parent which gives the child does not know about the key requirement for the component, therefore it creates a copy of the element it is given and adds a key based on some unique identifier in the object. For more info on what key does: https://facebook.github.io/react/docs/multiple-components.html
In fact, React.cloneElement is not strictly associated with this.props.children.
It's useful whenever you need to clone react elements(PropTypes.element) to add/override props, without wanting the parent to have knowledge about those component internals(e.g, attaching event handlers or assigning key/ref attributes).
Also react elements are immutable.
React.cloneElement( element, [props], [...children] ) is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, the children prop in React is especially used for containment (aka composition), pairing with React.Children API and React.cloneElement, component that uses props.children can handle more logic(e.g., state transitions, events, DOM measurements etc) internally while yielding the rendering part to wherever it's used, React Router <switch/> or compound component <select/> are some great examples.
One last thing that worth mentioning is that react elements are not restricted to props.children.
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
They can be whatever props that makes sense, the key was to define a good contract for the component, so that the consumers of it can be decoupled from the underlying implementation details, regardless whether it's using React.Children, React.cloneElement, or even React.createContext.

Resources