Rendering "a" with optional href in React.js - javascript

I need to render a table with a link in one of the columns, and searching for a most elegant way to do it. My main problem is - not all table rows are supplied with that link. If link is present - I need that "a" tag rendered. If not - no need for "a" tag at all. Generally speaking I would like react to handle that choice (render vs not render) depending on this.state.
This is what I have at the moment.
React.createClass({
getInitialState: function () {
return {
pipeline: this.props.data.pipeline,
liveUrl: this.props.data.liveUrl,
posted: this.props.data.created,
expires: this.props.data.end
};
},
render: function () {
return (
<tr className="posting-list">
<td>{this.state.pipeline}</td>
<td>Posted</td>
<td>
<input className="datepicker" type="text" value={this.state.posted}/>
</td>
<td>
<input className="datepicker" type="text" value={this.state.expires}/>
</td>
<td>UPDATE, DELETE</td>
</tr>
);
}
});
This results is DOM element :
XING_batch
This is not acceptable solution for me, because those blank hrefs are still clickable.
I also tried adding some logic to getInitalState(
liveUrl: (this.props.data.liveUrl !== "") ? this.props.data.liveUrl : "javascript:void;",
), which worked fine, but looks weird, and adds errors in console(Uncaught SyntaxError: Unexpected token ;)
The only way I've got left is creating 2 different components for

It's just JavaScript, so you can use any logic you like, e.g.:
<td>
{this.state.liveUrl
? <a ...>{this.state.pipeline}</a>
: this.state.pipeline}
</td>

You can choose the type of component at runtime, as well:
import * as React from "react";
const FooBar = props => {
const Component = props.href ? "a" : "div";
return (
<Component href={href}>
{props.children}
</Component>
);
};
<FooBar>Hello</FooBar> // <div>Hello</div>
<FooBar href="/">World</FooBar> // World

Take a look at spread properties:
You could use them like this for example:
var extras = { };
if (this.state.liveUrl) { extras.href = this.state.liveUrl; }
return <a {...extras} >My link</a>;
The values are merged with directly set properties. If they're not on the object, they're excluded.

Related

can't append h1 element to parent div in React?

i'm creating a simple react website that's supposed to do some calculations and find out Joules of my input values after the calculations...right now the input values are already preset but i will remove the value="" from my <input> later.
here is the .JSX component file that's the issue...one of the components.
import React, { Component } from 'react';
import Atom_icon from './cartridges.png';
class Joule_calc extends Component {
render(){
return (
<div className='Joule_div'>
<h3 style={{color:"white", textAlign:"center"}}>JOULE CALCULATOR</h3>
<label className='lab1'>WEIGHT=/GRAMS</label><br></br>
<input className='weight_inp' type='text' value="2" />
<label className='lab2'>SPEED=M/S</label><br></br>
<input className='speed_inp' type='text' value="5" />
<button className='count_button' onClick={this.Create_response}>CALCULATE</button>
<h1 className='Result_joule'></h1>
</div>
)
}
Create_response(){
console.log("creating response...")
let sum = document.createElement("h1")
sum.className = 'Result_joule'
sum.textContent = "678"
let div_panel = document.getElementsByClassName("Joule_div")
div_panel.append('Result_joule')
}
Returned_values(){
let weight_val = document.getElementsByClassName("weight_inp")[0].value;
let speed_val = document.getElementsByClassName("speed_inp")[0].value;
let final_calculation = weight_val * speed_val
return final_calculation
}
}
export default Joule_calc
so when i run my code i get
Uncaught TypeError: div_panel.append is not a function
at Create_response (Joule_calc_window.jsx:31:1)
i don't get why i can't append my new element to the div. it says it's not a function so what's the solution then? i'm new to React and web so probably it's just a noobie thing.
also i tried directly creating a h1 inside the 'Joule_div' like this.
<h1 className='Result_joule'>{"((try returning here from one of these methods))"}</h1>
but that of course failed as well. So would appreciate some help to get what's going on. i'm trying to add a number after the button click that's in h1 and in future going to be a returned number after calculating together the input values in a method.i imagine that something like
MyMethod(){
value = values calculated
return value
}
and later grab it with this.MyMethod
example
<h1>{this.MyMethod}</h1>
this is a example that of course didn't work otherwise i wouldn't be here but at least gives you a clue on what i'm trying to do.
Thank you.
You don't leverage the full power of react. You can write UI with only js world thanks to JSX. State changes triggering UI update.
I may miss some specificaiton, but fundamental code goes like the below. You should start with function component.
// Function component
const Joule_calc = () =>{
// React hooks, useState
const [weight, setWeight] = useState(0)
const [speed, setSpeed] = useState(0)
const [result,setResult] = useState(0)
const handleCalculate = () =>{
setResult(weight*speed)
}
return (
<div className="Joule_div">
<h3 style={{ color: 'white', textAlign: 'center' }}>JOULE CALCULATOR</h3>
<label className="lab1">WEIGHT=/GRAMS</label>
<br></br>
<input className="weight_inp" type="text" value={weight} onChange={(e)=>setWeight(parseFloat(e.target.value))} />
<label className="lab2">SPEED=M/S</label>
<br></br>
<input className="speed_inp" type="text" value={speed} onChange={(e)=>setSpeed(parseFloat(e.target.value))} />
<button className="count_button" onClick={handleCalculate}>
CALCULATE
</button>
<h1 className='Result_joule'>{result}</h1>
</div>
)
}
export default Joule_calc;
div_panel is an collection of array which contains the classname ["Joule_div"]. so first access that value by using indexing . and you should append a node only and your node is "sum" not 'Result_joule' and you should not use textcontent attribute because you will be gonna definitely change the value of your result as user's input value
Create_response(){
console.log("creating response...")
let sum = document.createElement("h1")
sum.className = 'Result_joule'
//sum.textContent = "678"
let div_panel = document.getElementsByClassName("Joule_div")
div_panel[0].append('sum')
}
if any problem persists , comment below

Render class conditionally in React

I'm trying to render a class conditionally. If the mapped item is blank, I'd like there to be a class that renders. Otherwise, no changes. I'm sure this very simple but I'm new at this and not sure how to identify the blank item. Is this a problem with scope? This is the code in my component:
const TableBody = (props) => {
let classes = ''
classes += (props.data.map === '') ? '' : 'collapse'
return (
<tbody>
{props.data.map((item, index) => (
<tr key={typy(item, 'sys.id').safeString || index}>
{props.columns.map(column =>
<td className={classes} role='cell' key={column.label}>{typy(item, column.path).safeObject}</td>)
}
</tr>
))}
</tbody>
)
}
All of the <td> elements are collapsed so the code I'm using above must not be properly detecting a blank value. Can anyone point me in the right direction here?
Per my comment, props.data appears to be an array. You are checking to see if props.data.map === '', which will always evaluate to false. You should probably fix that statement, otherwise the class will always be 'collapse'. Hope that helps!

react.js how to get prop form tree element

I am trying to make table cell editable after clicking on icon in another cell , for that I need to get index of element so the editor will open in the correct row , which icon belongs to.
My issue is that I dont know the way i should get the prop value of table DOM element here is code for for clearify
a part of dom tree generated with react:
<tbody>
{stepsDone.map(function(step,idx) {
let content = step;
const editing = this.state.editing;
if(editing){
content = (
<form onSubmit={this._save}>
<input type="text" defaultValue={step} />
</form>
);
}
return(
<tr key={idx}>
<td className="step" data-step={'step'+idx}>{content}</td>
<td className="icRow">
<Icon className="edit" onClick={this._showEditor} rownum={idx}/>
<Icon className="remove"/>
<Icon className="trash outline"/>
</td>
</tr>
)
},this)}
show editor function:
_showEditor(e){
this.setState({
editing:{
row:e.target.rownum
}
});
console.log(this.state.editing);
}
After execution of showedtior function console logs :
first click = null , which is normal i think
more clicks = undefined , and thats whats brings a trouble i want to receive idx from map function.
here is code from Icon.js
import React from 'react';
import classNames from 'classnames';
export function Icon(props) {
const cssclasses = classNames('icon', props.className);
return <i className={cssclasses} onClick={props.onClick}/>;
}
if you want to reveive the idx from the map function you should pass it to the function _showEditor so your code must be like this :
<Icon className="edit" onClick={this._showEditor(idx)}/>
and the function definition should be :
_showEditor = (idx) => (event) => {
this.setState({
editing:{
row:idx
}
});
console.log(this.state.editing);
}
or if you don't want to use the arrow functions for some reason, just replace
onClick={this._showEditor(idx)}
with
onClick={this._showEditor.bind(this,idx)}
and its definition becomes
_showEditor(idx){...}

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

How to pass a React component (or raw html) into other React component and insert it?

I have raw html markup returned from ajax. Rendering raw html is not easy in React. I tried to use their dangerouslySetInnerHTML up and down but no go, only errors are thrown from React. That dangerouslySetInnerHTML is truly obscure.
So I decided to compile my raw html to a React component and insert it. Raw html is compiled to React component all right but I cannot insert it. Here is my code. I want to pass <Html /> component (or raw html would be even better) to <Tr /> component as this.props.b:
var FooComponent = React.createClass({
render: function() {
var Html = React.createClass({
render: function() {
return (
myRawHtmlMarkup
);
}
});
return (
<Tr id={key} a={value} b={Html} />
);
}
});
Now how to insert either raw html or <Html /> into DOM <td> element? Using {this.props.b} doesn't work.
And the inportant thing here is that I cannot change this.props.b to smth else like this.props.child, this.props.children, etc. I need that this.props.b because there is a loop in parent FooComponent component and I assign values to this.props.b using many if/else conditions (not shown above for the sake of simplicity):
var Tr = React.createClass({
render: function() {
return (
<tr>
<td className="idCol">{this.props.id}</td>
<td className="aCol">{this.props.a}</td>
<td className="bCol">{this.props.b}</td>
</tr>
);
}
});
The render method in your Html component will throw an error, because a component needs to return a valid ReactComponent, not a string.
I think you could parse the HTML and append as children in componentDidMount: https://jsfiddle.net/4z13gp7g/
var Tr = React.createClass({
componentDidMount: function(){
var div = document.createElement('div');
div.innerHTML = this.props.b;
this.refs.b.appendChild( div.firstChild )
},
render: function() {
return (
<tr>
<td className="idCol">{this.props.id}</td>
<td className="aCol">{this.props.a}</td>
<td className="bCol" ref="b"></td>
</tr>
);
}
});
ReactDOM.render(
<Tr id="key" a="value" b="<p><strong>Hi</strong></p>" />,
document.querySelector('table tbody')
);
Do you need a react class for Html var?
If not, try this:
var FooComponent = React.createClass({
render: function() {
var Html = (myRawHtmlMarkup);
return (
<Tr id={key} a={value} b={Html} />
);
}
});

Categories

Resources