Appending a new dom element in react - javascript

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.

Related

How can I make a JavaScript (with React framework) button create a new instance of a class?

I am new to web development and I only now make my first page (using ReactJS on top of it), so sorry if this is a noob question. In my page I have created 3 windows which are going to be filled with information later on, and I want the user to be able to make their own ones by clicking a "+" button. Though, for some reason, my code does not work. Even after one clicks at the "+" button, nothing happens. Here's the code of my 'Board' class for demonstration.
class Board extends React.Component {
renderWindow(i) {
return (
<Window name={i} />
);
}
handleClick() {
var test = document.createElement("P");
test.innerHTML = "this.renderWindow('test-thingy')";
document.body.appendChild(test);
}
render() {
return (
<div>
<div className="info-window">
{this.renderWindow("Contacts")}
{this.renderWindow("Accounts")}
{this.renderWindow("Info")}
</div>
<button
className="button1"
onClick={() => this.handleClick()}
>
{"+"}
</button>
</div>
);
}
}
I just don't know how to make it respond accordingly, because right now it only creates a text with the quote from test.innerHTML. I have tried several other syntaxes for that line, such as:
test.innerHTML = "{this.renderWindow('test-thingy')}";
or
test.innerHTML = "<div> {this.renderWindow('test-thingy')} </div>";
or even inverting the " and '. I have been searching ever since for a way, but I am unable to find it. I want it to create a fourth window with the name 'test-thingy'.
You should put the JSX element to be rendered due to change state as part of the render method.
const WindowComp = ({ name }) => <p>{name}</p>;
class Board extends React.Component {
state = {
windowNames: ['Contacts', 'Accounts', 'Info']
};
handleClick() {
this.setState({
windowNames: [...this.state.windowNames, 'test-thingy']
});
}
render() {
return (
<div>
<div className="info-window">
{this.state.windowNames.map(
(name, i) => <WindowComp key={i} name={name} />
)}
</div>
<button
className="button1"
onClick={() => this.handleClick()}
>
{"+"}
</button>
</div>
);
}
}
ReactDOM.render(<Board />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class="react"></div>

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>
);

Target dom element of react component without ref

I installed a react-component called react-geosuggest in my project. I need to find the <label> dom element in the <Geosuggest> component and insert an onClick handler to <label>. Is there a way to target the <label> tag without altering the <Geosuggest> component's code.
Here's the JSX
render() {
return (
<div>
<Geosuggest
id = "searchbar"
label = " "
placeholder = ""
initialValue = {this.state.location}
onChange = {this.onInputChange}
onSuggestSelect = {this.onSelection}
onClick = {this.toggleSearch}
className = {this.state.expand}
inputClassName = {this.state.expand}
// Name a calling reference
ref = 'geosuggest_component'
/>
</div>
);
}
Here's the HTML output
<div class="geosuggest">
<div class="geosuggest__input-wrapper">
<label for="searchbar"></label>
<input class="geosuggest__input" type="text" id="searchbar">
</div>
</div>
You have to use vanillajs. In your component, you can access geosuggest_component ref in componentDidMount:
componentDidMount() {
// findDOMNode returns an HTMLElement
const node = ReactDOM.findDOMNode(this.refs.geosuggest_component);
// Then search the label
const label = node.querySelector('label');
// Now, you can do anything you want with label
label.addEventListener('click', () => console.log('clicked'))
}
I use ReactDOM.findDOMNode because in this case, this.refs.geosuggest_component returns a React component.
The method findDOMNode will give you the HTMLElement you need.
However, a better way to declare a ref is by using a callback:
ref={node => this.geosuggest_component = node}
Now in componentDidMount:
const node = ReactDOM.findDOMNode(this.geosuggest_component);

Passing JSX to components vs dangerouslySetInnerHTML

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!

React component with two sets of children

I'm creating a component that needs to take in two sets of children and to be placed in two different parts of a component.
let CreditCardForm = ({icons, fields}) => (
<div>
<div className='some'>
<div className='special'>
<div className='nesting'>
{icons}
</div>
</div>
</div>
{fields}
</div>
)
let CreditCardFormUsage = () => {
let icons = () => (
<Icons>
<IconBogus/>
<IconVisa/>
<IconPaypal/>
<IconMore/>
</Icons>
)
let fields = () => (
<CreditCardFields>
<FieldCardNumber/>
<FieldName/>
<FieldExpirey/>
<FieldCCV/>
</CreditCardFields>
)
return (
<CreditCardForm icons={icons} fields={fields}/>
)
}
The code above should work, my question is it possible to grab those property values based on the children in the element itself, and have something more natural?
<CreditCardForm>
<Icons>
<IconBogus/>
<IconVisa/>
<IconPaypal/>
<IconMore/>
</Icons>
<CreditCardFields>
<FieldCardNumber/>
<FieldName/>
<FieldExpirey/>
<FieldCCV/>
</CreditCardFields>
</CreditCardForm>
Yes, this.props.children will return an array so if you always want to load specific children, then just reference those children by index in your wrapper. Then you could just turn icons and fields into wrapper components. Here is a working jsfiddle. See how the render method of App is exactly what you want.
CreditCardForm render:
<div>
<div className='some'>
<div className='special'>
<div className='nesting'>
{this.props.children[0]}
</div>
</div>
</div>
{this.props.children[1]}
</div>
Fields and Icons render:
<div>{this.props.children}</div>
App render:
<CreditCardForm>
<Icons>
<IconBogus />
</Icons>
<Fields>
<FieldCardNumber />
<FieldName />
</Fields>
</CreditCardForm>
yes, you can do it with child props. Read more #docs:
https://facebook.github.io/react/tips/children-props-type.html
And of course check out React.Children
https://facebook.github.io/react/docs/top-level-api.html#react.children

Categories

Resources