Can you use jsx within template string in React render props? - javascript

Is it possible to put JSX inside a template string that is being used as a React render prop?
This is what I'm trying to do, but it leads to the link rendering as [object Object]
const Container = ({ message }) => <div className="from line 4"> {message}</div>;
const Link = () => juan;
const App = () => (
<div>
<Container message={`My message with a ${<Link />}`} />
</div>
);
One thing I tried was to put JSX instead of a template string inside message. This works, but it introduces a new div that isn't needed.
<Container
message={<div>My message {<Link />}</div>}
/>
I made this codesandbox to illustrate the problem

You can use a Fragment to render inline like you are trying to do and to prevent adding a new wrapping <div />:
const App = () => (
<div>
<Container
message={<React.Fragment>My message with a <Link /></React.Fragment>}
/>
</div>
);
Here is a forked version of your Codesandbox using React.Fragment: https://codesandbox.io/s/nrmr9l34vl

Related

"Each child in a list should have a unique "key" prop" [duplicate]

I'm building an app using the Google Books API and I appear to be passing a unique key to each child in the list, but the error won't go away. I must be doing something wrong but I'm not sure what.
const BookList = (props) => {
//map over all of the book items to create a new card for each one in the list
const books = props.books.data.items.map((book) => {
console.log(book.id);
return (
<div className="col col-lg-4 grid-wrapper">
<BookCard
key={book.id}
image={book.volumeInfo.imageLinks.thumbnail}
title={book.volumeInfo.title}
author={book.volumeInfo.authors[0]}
description={book.volumeInfo.description}
previewLink={book.volumeInfo.previewLink}
buyLink={book.saleInfo.buyLink}
/>
</div>
);
});
return <div>{books}</div>;
};
Notice that after the return in const books I have a console.log(book.id), which will display all 10 unique id keys in the console. But when I try to pass it to the child of this component using key={book.id}, I get this error.
The key needs to go on the outermost returned element. In your specific case, that means changing this:
<div className="col col-lg-4 grid-wrapper">
<BookCard
key={book.id}
to this:
<div className="col col-lg-4 grid-wrapper" key={book.id}>
<BookCard
I was using React fragments in my map() call in their simple syntax form, and was running into the same warnings with the code below:
<>
<h3>{employee.department}</h3>
<TableRow
key={employee.id}
cellValues={["Name", "Title"]} />
<TableRow
key={employee.id}
cellValues={[employee.name, employee.title]}
/>
</>
Building off the accepted answer, I realized I needed the outermost element to have the ID. I learned of an alternate syntax for React fragments that allows one to put an ID on it. The resulting code below caused the warnings to go away:
<React.Fragment key={employee.id}>
<h3>{employee.department}</h3>
<TableRow
cellValues={["Name", "Title"]} />
<TableRow
cellValues={[employee.name, employee.title]}
/>
</React.Fragment>

How to render html element in pre / REACT

Hello I am working on a blog post creation tool and I need a engine when I write in textarea <hr/> I get a line, or when I write <img/> I get an image but it doesn't render.
The post is written in a textarea and should be displayed in a div.
How to do it?
const PostCreate = () => {
const [postValue, changeValue] = useState('')
const handleChangeValue = (e:any) => {
changeValue(e.target.value)
console.log(postValue);
}
return (
<div className='postCreate'>
Create New Post
<textarea onChange={handleChangeValue} value={postValue}/>
<div>
{postValue}
</div>
</div>
)
}
If I write <hr/> I get the string <hr/> instead of a line:
You can use dangerouslySetInnerHTML prop for the div.
<div dangerouslySetInnerHTML={{ __html: postValue }} />
You can check it on this document.

Render HTML inside a Material-UI Card with React

I have the following < CardHeader /> component inside a < Card />.
<CardHeader
title={card.title}
subheader={`${moment(card.createdAt).startOf('minute').fromNow()}` + ' by ' + <div>ABC</div>}/>
It renders the following way:
18 minutes ago by [object Object].
What is the right way of doing this? Thanks in advance!
You can pass on a node to CardHeader's subHeader props, just don't mix string and html
<CardHeader
title={card.title}
subheader={<div>{moment(card.createdAt).startOf('minute').fromNow()} by ABC</div>}/>
Sample DEMO
Wrap your heading in a fragment
You can separate out part of your logic to its own level. This will make reuse easier, and testing also easier.
const MyHeading = ({card}) => {
return (
<React.Fragment>
{`${moment(card.createdAt).startOf('minute').fromNow()} by`
<div>ABC</div>
</React.Fragment>
)
}
In the above I'm only retrieving card, but you can pass all sorts of data to your heading component.
<CardHeader
title={card.title}
subheader={<MyHeading {...props} />
/>

How to inject a dinamically created element into an existing div in React JSX?

I have a list of objects photos, from a json data file, that I would like to organize into 3 different <div> columns, but I dont know how to achieve that, here is my broken non-optimized code:
<div className="container">
<div ref={leftColRef} className="left-col" />
<div ref={centreColRef} className="centre-col" />
<div ref={rightColRef} className="right-col" />
{Object.keys(photos).forEach((n, i) => {
const id = photos[n].id;
const thumb = photos[n].thumbnailUrl;
const title = photos[n].title;
const element = (
<Thumbnail id={id} title={title} thumb={thumb} />
);
if (i % 3 === 0) {
leftColRef.current.append(element);
} else if (i % 3 === 1) {
centreColRef.current.append(element);
} else {
rightColRef.current.append(element);
}
// this line works, it idsplays the data but is commented as the data needs to go inside its respective columns
// return <Thumbnail key={id} title={title} thumb={thumb} />;
})}
</div>
The idea is to insert some elements into the left-column when i%3 = 0 and others in the centre-column when i%3 = 1 and so on ...
And a link to my codesandbox
Any help/advise will be much appreciated.
Easiest is probably to prepare the data outside the render function and to render the column one by one.
You should not manipulate the DOM like it's done in jQuery using JSX
Example:
const Component = (props) => {
const filterPhotos = (column) => {
return props.photos.filter((photo,index)=> index%3==column);
}
return <>
<MyColumn photos={filterPhotos(0)}/>
<MyColumn photos={filterPhotos(1)}/>
<MyColumn photos={filterPhotos(2)}/>
</>;
}
First, using ref on div to inject stuff on it is wrong. It's the opposite of how react works.
Like charlies said, I would split the photos in 3 different arrays before the render. Then, you'll be able to do something like this :
<div ref={leftColRef} className="left-col" />
{ photosLeft.map(photo => <Thumbnail key={photo.id} {...photo} />)
</div>
when preparing your data, try to use the same object properties and component props name so you can spread it easily ( {...photo} ).
Note: Also, when rendering an array in react, each child must have a unique key props. It will help react to render on that part of dom if your data change.

Render a component within another in React

When a state is changed, React triggers componentDidUpdate() method, and by then I do:
componentDidUpdate: function () {
React.render(new SubmitButton, $('.uploader-submit').get(0));
}
As you saw, I'm rendering a SubmitButton when a specific state is changed, but my question is: is this the best behavior to get this feature done?
My scenario is: I'm uploading a photo. When the input[type=file] is changed, I create a new state property and then the componentDidUpdate() is triggered, invoking the SubmitButton.
This is my render() method:
render: function () {
return (
<div className="uploader">
<header className="uploader-header">
<div className="uploader-actions pull-left">
<div className="uploader-submit"></div>
<CancelButton router={this.props.router} />
</div>
<UploadButton callback={this.imageSelectedCallback} />
</header>
<Preview imageUri={this.state.imageUri} />
</div>
)
}
Couldn't I do something like the <Preview /> component? I mean, it is there, but something just appears when this.state.imageUri is different of null. This is the implementation of Preview:
var Preview = {
render: function () {
return (
<img src={this.props.imageUri} />
)
}
};
module.exports = React.createClass(Preview);
Yes, I know — "Preview" is invisible by default because it is an image, but I want to know if there's another approach to reach what I want: to show something based on a state, using the render method.
React doesn't render falsy values, be it a component or an attribute (like in the Preview case), e.g.
<div>{null}</div>
<img src={null} />
renders to
<div></div>
<img/>
So typically you just create a variable and conditionally assign it a component or null as was also suggested in another answer:
var button = null;
if(myConditionForShowingButton) {
button = <SubmitButton />;
}
-- or simply --
var button = myConditionForShowingButton ?
<SubmitButton /> :
null;
In cases where the component gets bigger it's typically more readable and cleaner to have a subroutine for rendering that part
var complexComponent = condition ?
this.renderComplexComponent() :
null
Yes. If-Else in JSX.
render: function () {
var submitButton;
if (this.state.imageSelected)
submitButton = <SubmitButton />;
return (
<div className="uploader">
<header className="uploader-header">
<div className="uploader-actions pull-left">
<div className="uploader-submit">{ submitButton }</div>
<CancelButton router={this.props.router} />
</div>
<UploadButton callback={this.imageSelectedCallback} />
</header>
<Preview imageUri={this.state.imageUri} />
</div>
)
}

Categories