Passing JSX to components vs dangerouslySetInnerHTML - javascript

I've been working through react examples, and I've been hacking away building some components. Now I feel i'm running into a fundamental "Brain Fart", regarding component structure and nesting.
What I'm after:
Input component with Optional Labels and help text.
What I have right now: ( which does work )
Input.js
//...//
var MyInput = React.createClass( {
render: function() {
//...//
var helpText = null;
if( typeof this.props.helpText !== 'undefined' ){
helpText = <p className="help-block" > {this.props.helpText} </p>;
}
return (
<div className={ className }>
<MyLabel showLabel={ this.props.showLabel} htmlFor={ this.props.name }>
{ this.props.title }
</MyLabel>
<input
type={ this.props.type || 'text' }
name={ this.props.name }
onChange={ this.changeValue }
value={ this.getValue() }
checked={ this.props.type === 'checkbox' && this.getValue() ? 'checked' : null }
placeholder={ this.props.title }
/>
<span className='validation-error'>{ errorMessage }</span>
{helpText}
</div>
);
}
});
module.exports = MyInput;
LoginForm.js
//...//
var LoginForm = React.createClass({
// ... //
render: function() {
return (
<Form className=" col-sm-11 col-lg-10 block-center loginFrm" >
<div className="row">
<FrmInput value =""
name="username"
title="Username"
className="col-sm-5"
showLabel={false}
helpText= { <span> Help text with link </span>}
required />
<FrmInput value =""
type="password"
name="password"
title="Password"
className="col-sm-5"
showLabel={false}
required />
<button type="submit"
className="btn btn-default input-sm "
>
Sign In
</button>
</div>
<div className="row">
<div className="pull-right" >
<FrmCheckbox name="rememberMe"
title="Remember Me"
/>
</div>
</div>
</Form>
);
},
});
module.exports = LoginForm;
Making the label optional was easy. I use a BOOL showLabel property on the <MyInput/> component and pass that into the MyLabel component. showLabel is assumed TRUE, so the label is shown unless you set showLabel to false as seen above ( then <MyLabel/> just returns NULL ).
I first tried a similar method with a <help/> component to add the optional help text after the input inside <MyInput/>. Everything worked until I added a link inside the help text. Researching I found dangerouslySetInnerHTML as a means to pass HTML content into a component. While testing I also found the code above appears to work also, though I'm not exactly sold on why and how "good" this approach is.
In short it appears I'm just passing JSX objects into my component for rendering. inside <Form> (from LoginForm.js ) on the <FrmInput/> component there is a property named helpText set as follows
helpText= { <span> Help text with link </span> }
inside the <MyInput/> component I'm testing/listening for the helpText property and setting it to a variable when found (Again wrapping with JSX)
var helpText = null;
if( typeof this.props.helpText !== 'undefined' ){
helpText = <p className="help-block" > {this.props.helpText} </p>;
}
Then in the Render Method I have { helpText }
All in all it looks like I'm just passing javascript objects ( via JSX ) on through untill the final render method. I have not seen the above used in tutorials or documentation, so I'm just looking for a professional opinion.
Is the above "good" practice or how could this better be handled.

There's nothing 'wrong' with your approach. A few suggestions that can help stream line a bit.
You can shorten this block to a simple inline ternary:
var helpText = null;
if( typeof this.props.helpText !== 'undefined' ){
helpText = <p className="help-block" > {this.props.helpText} </p>;
}
You can remove the above and in your render replace {helpText} with:
{ this.props.helpText ? this.props.helpText : null }
In form input remove the inline helpText html and move to a variable using parens for the JSX.
const helpTextContent = ( <span> Help text with link </span> );
Then inline: helpText = { helpTextContent }
Lastly if you're using ES6 you can use the following syntax to make using props less cumbersome:
let { helpText, someOtherProp, anotherProp } = this.props;
Then you can just refer to helpText or someOtherProp directly without the this.prop every time.
Hope that helps!

Related

Display React component only when attribute is not null

MyReactComponent returns a hyperlink and a message after looking up an API using the name value. However I would like to render this component (which includes making the API call) only when name is not empty. Would the below solve the purpose or should I approach this differently?
<dt hidden={null == name || '' === name.trim()}>
<MyReactComponent name={name || ' '} />
</dt>
Here is another way you can do this :
{(name && name.trim() !== '') &&
<MyReactComponent name={name} />
}
You can learn more here.
Instead of using hidden attribute, you can render your component with ternary operator:
{name ? <MyReactComponent name={name} /> : null}

Validating a value when button is pressed before passing it

Hi I am new to React and I am a little bit confused on how to validate a value before passing it to the partialRefund function I have.
I am trying to set a simple validation to make sure the value is not empty and numeric before passing it to the partialRefund function.
The first line is the code I currently have. The second line is the code I am trying to write for the validation but it is not working.
Any help would be really appreciated! Thanks!
//Current code
{partialRefundSelected ? <div> <input id={`partial_refund_${order_id}`} type='text'/> <button onClick={() => partialRefund(order_deets_obj,"partialRefund",document.getElementById('partial_refund_'+order_id).value)}> Refund Order </button> </div> : ""}
//Code I am trying to use
{partialRefundSelected ? <div> <input id={`partial_refund_${order_id}`} type='text'/> <button onClick={(validateValue(document.getElementById('partial_refund_'+order_id).value)) => partialRefund(order_deets_obj,"partialRefund",document.getElementById('partial_refund_'+order_id).value)}> Refund Order </button> </div> : ""}
On the second line i am trying to pass a function that will validate the value and the pass it to the partialRefund function. But it doesnt seem to be working :(
Use this:
{
partialRefundSelected ?
<div>
<input id={`partial_refund_${order_id}`} type='text'/>
<button onClick={() => {
const validatedValue=validateValue(document.getElementById('partial_refund_'+order_id).value));
partialRefund(order_deets_obj,"partialRefund",validatedValue);
}}> Refund Order
</button>
</div> :
""}
You can do the validation in the onClick callback if you add curly brackets around the parttialRefund call.
export default function App() {
const partialRefundSelected = true;
const order_id = 1;
const order_deets_obj = { deets: "good deets" };
const partialRefund = (deets, someString, someValue) => {
console.log(deets, someString, someValue);
};
return partialRefundSelected ? (
<div>
<input id={`partial_refund_${order_id}`} type="text" />
<button
onClick={() => {
const value = document.getElementById("partial_refund_" + order_id)
.value;
// Do validation here
if (value === "I LOVE CATS") {
partialRefund(order_deets_obj, "partialRefund", value);
}
}}
>
Refund Order
</button>
</div>
) : (
""
);
}
While this is an option in react, I would suggest making your input a Controlled Component. This would allow you to keep the input's text in state instead of needing to pull the text off of the element after a click. Here is an example.

Appending a new dom element in react

I've created a custom component and I want to add child element when the component renders if a certain property is set to be true. I used the following code, but the component is not rendered. what am I doing wrong here.
let deleteNode = '';
if(deletable){
deleteNode = '<div />'
}
let defaultClasses = 'chips chips-rounded';
return (
<div className={classNames(classes, defaultClasses)} onClick={ this.onClick }>
{avatar}
<span>{this.props.labelText}</span>
{deleteNode}
</div>
)
You are trying to render a component but actually you are just sending string in your deleteNode. Your code should be something like below
if(deletable){
deleteNode = (<div />);
}
I different approach would be:
render() {
const defaultClasses = 'chips chips-rounded'
return (
<div className={classNames(classes, defaultClasses)} onClick={ this.onClick }>
{avatar}
<span>{this.props.labelText}</span>
{deletable && <div />}
</div>
)
}
So you don't need the extra if checking.

How to add a <br> tag in reactjs between two strings?

I am using react. I want to add a line break <br> between strings
'No results' and 'Please try another search term.'.
I have tried 'No results.<br>Please try another search term.'
but it does not work, I need to add the <br> in the html.
Any ideas how to solve it?
render() {
let data = this.props.data;
let isLoading = this.props.isLoading;
let isDataEmpty = Object.entries(data).length === 0;
let movieList = isLoading ? <Loader /> : isDataEmpty ? 'No results. Please try another search term.' :
Object.entries(data).map((movie, index) => <MovieTile key={index} {...movie[1]} />);
return (
<div className='movieList'>{movieList}</div>
);
}
You should use JSX instead of string:
<div>No results.<br />Please try another search term.</div>
Because each jsx should have 1 wrapper I added a <div> wrapper for the string.
Here it is in your code:
render() {
let data = this.props.data;
let isLoading = this.props.isLoading;
let isDataEmpty = Object.entries(data).length === 0;
let movieList = isLoading ? <Loader /> : isDataEmpty ? <div>No results.<br />Please try another search term.</div> :
Object.entries(data).map((movie, index) => <MovieTile key={index} {...movie[1]} />);
return (
<div className='movieList'>{movieList}</div>
);
}
You can use CSS white-space to solve the problem.
React Component
render() {
message = `No results. \n Please try another search term.`;
return (
<div className='new-line'>{message}</div>
);
}
CSS
.new-line {
white-space: pre-line;
}
OUTPUT
No results.
Please try another search term.
break text to line:
render() {
...
<div>
{this.props.data.split('\n').map( (it, i) => <div key={'x'+i}>{it}</div> )}
</div>
...
Some HTML elements such as <img> and <input> use only one tag. Such tags that belong to a single-tag element aren't an opening tag nor a closing tag. Those are self-closing tags.
In JSX, one has to include the slash. So, remove <br> and try <br />
Here is how I got around this. Let message be the prop/variable that has the string containing line breaks to be displayed in HTML as follows:
message = 'No results.<br>Please try another search term.';
<div>
{message}
</div>
To make this work, we need to use \n instead of break tag <br> and set the following css on the wrapper element of this message as follows:
message = 'No results.\nPlease try another search term.';
<div className="msg-wrapper">
{message}
</div>
CSS:
.msg-wrapper {
white-space: pre-wrap;
}
OUTPUT:
No results.
Please try another search term.
If you don't want put the string inside a <div> you could use <> to do it.
Like this:
var text = <>This is a text in the first line;<br />this is a text in a second line</>;
Just split text by /n, I do this in this way:
<div>
{text.split('\n').map((item, i) => <p key={i}>{item}</p>)}
</div>
Try with span
return (
<div className='movieList'><span>{movieList}</span></div>
);
If you are like in my situation and you don't want to add css, you can do that :
render () {
...
return (
...
<Typography component="p">
...
{(contact.lastname)?<div>Hello {contact.firstname} {contact.lastname}</div>:''}
...
</Typography>
...
);
}
using ` worked for me however i am not sure if it is the exact solution to the problem :
import React from 'react';
import ReactDOM from 'react-dom';
let element = (
<div>
<h1> Hello world</h1>
This is just a sentence <br></br>
But This line should not be in the same previous line. <br></br>
The above content proves its working. <br></br>
npm v6.14.6 | react : {React.version}
</div>
);
ReactDOM.render(element,document.getElementById("html-element-id"))
You can add a span tag and add block as a class.
Pomodoro Technique Timer <span className="block">with Bla</span>
The simplest thing which I did is by creating a component.
const EmptySpace = ({ spaceCount = 0 }) => {
return (
<>
{Array.from({ length: spaceCount }, (item, index) => {
return <br key={index} />;
})}
</>
);
};
export default EmptySpace;
<EmptySpace spaceCount={1} />
In your case you could do something like this:
const msg = (
<p>
No results <EmptySpace spaceCount={2} />
Please try another search term.
</p>
);

JSX with a HTML tag from a variable

I have a React component defined in JSX which returns a cell using either td or th, e.g.:
if(myType === 'header') {
return (
<th {...myProps}>
<div className="some-class">some content</div>
</th>
);
}
return (
<td {...myProps}>
<div className="some-class">some content</div>
</td>
);
Would it be possible to write the JSX in such a way that the HTML tag is taken from a variable? Like:
let myTag = myType === "header" ? 'th' : 'td';
return (
<{myTag} {...myProps}>
<div className="some-class">some content</div>
</{myTag}>
);
The above code returns an error:
"unexpected token" pointing at {.
I am using Webpack with the Babel plugin to compile JSX.
Try setting your component state and rendering like so:
render: function() {
return(
<this.state.tagName {...myProps}>
<div className="some-class">some content</div>
</this.state.tagName>
);
},
You can do something like:
const content = <div> some content </div>
return (
{myType === 'header'
? <th>{content}</th>
: <td>{content}</td>
}
)
Note that this does not really solve your question about "dynamic tag" but rather the problem you seem to have.
The first answer did not work for my case so I solved it in another way.
From React documentation each element converts to pure JS like this.
So it is possible to create elements for React component that are dynamic like this:
let myTag = myType === "header" ? 'th' : 'td';
React.createElement(
myTag,
{className: 'some-class'},
<div className="some-class">some content</div>
)

Categories

Resources