for loop not working in render function reactjs - reactjs

m looping using for in render function
its giving syntax error can we not use this way in react
following is the code
render(){
return(
<table>
for(var i=0;i<this.props.TRlength;i++)
<TRcomponent TDlength={this.state.tdLength} />
</table>
)
}
error it throws
`/src/Table.js
Syntax error: D:/my-app1/src/Table.js: Unexpected token (17:50)
<table>
for(var i=0;i<this.props.TRlength;i++)//error here
^
<TRcomponent TDlength={this.state.tdLength} />
`
any help is appreciated

React doesn't play well with for loops in the render. You cannot create children for a parent that does not exist yet. You need to create the children first.
This article goes into detail about this issue.

You can store it in a variable and then use it in jsx
render() {
var rows = [];
for (var i=0;i<this.props.TRlength;i++) {
// note: we add a key prop here to allow react to uniquely identify each
// element in this array. see: https://reactjs.org/docs/lists-and-keys.html
rows.push(<TRcomponent TDlength={this.state.tdLength} key={i} />);
}
return < table >{rows}</table>;
}

Related

Setting span labels with a loop

I pulled data with request and now I'm trying to present each result on my site. I get an array with genres each time, so I need some ideas on how I could use loops in JSX in order to put those spans in result div that already has images, heading and stuff.
setGenres = () => {
let data = this.props.data.genres;
let labelsText = '';
for (let i = 0; i < data.length; i++) {
labelsText += <span className='genre'>data[i]</span>
}
return (labelsText);
}
<div className='result-info'>
<h4>{this.props.data.name}</h4>
{this.setGenres()}
</div>
Back when I was using vanilla JS, I could use string and put it via innerHTML, but now I have no idea what to do. It just returns [Object object] on my site.
You can use map to iterate over the array and return another result. So it's possible to return a portion of JSX which will be rendered inside your div:
<div className='result-info'>
<h4>{this.props.data.name}</h4>
{this.props.data.genres.map((genre,idx)=>(
<span key={idx} className='genre'>{genre}</span>
)}
</div>
JSX generates element classes for React to use. React allows rendering of various things, including arrays of React classes. In your scenario setGenres needs to change to the following.
setGenres = () => {
const data = this.props.data.genres;
const spans = data.map(genre => (<span key={genre} className='genre'>{genre}</span>));
return spans;
}
As noted by another user, I added the key prop as react will complain otherwise. It's best to set the key to something guaranteed to be unique, rather than just an index.
You can read more about array mapping here - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
You can read more about lists in react here - https://reactjs.org/docs/lists-and-keys.html

React + Firebase - Deleting firebase object inside mapped components

I'm trying to pass the key of my project object through to my mapped components in order to delete them.
UPDATE: using the _.forEach(project, (proj, key) => <Project key={key} {... more props here}/>) Throws an error about objects not being valid react children. I'm guessing this is because the project i'm forEaching over needs to be turned into an array? I keep trying to format in componentWillMount() but when i try to run forEach with setState and push to a new array i keep getting duplicates
componentDidMount() {
projectRef.on('value', snap => {
this.setState({projects: snap.val()})
// somehow i need to create a new array of objects that include the key.
})
}
UPDATE: i removed the codepen example. I like code sandbox better. Much better. =)
And here's the code sandbox (If you get an error about the [DEFAULT] app already being defined just refresh the output browser and it will work. I don't know why it's doing that... oh well. I added my attempt with forEach on the code sandbox example. Hopefully someone can let me know what i'm doing wrong.
Yep, map returns an array of the values of the object, in this case an object you can then access via the props in the <Display /> component, but not the key of each element of the object.
Perhaps you could use lodash's forEach in order to loop and have access to both the key and the value of each element in your data collection. Like that you can pass the key (that will be the target for the remove method) as a specific prop and the value as the item prop in the component.
export default class extends React.Component {
constructor() {
super()
this.state = {
items: null
}
}
render() {
return() {
<div>
{_.forEach(this.state.items, (item, key) =>
<Display key={key} itemKey={key} item={item}/>
)}
</div>
}
}
}
// then the display component
removeItem() {
firebase.database().ref({this.props.itemKey}).remove();
}
render() {
return (
<div>{this.props.item.name} <button onClick={this.removeItem}>X</button>
)
}
Here's a simple live example of how forEach works:
https://jsbin.com/xolejoj/edit?js,console
Edit 08-12-2017
The problem with your code is that you're missing the fact that map is returning the key and the value of each element of the object. In this case your object has a key string and a value that is the object. Then on your JSX you're trying to pass the key as it were a part of the object (value) but is not, therefore you're getting an undefined value in the component's props.
Change your code to this:
<div>
{_.map(projects, (proj, key) => <Project
key={key}
title={proj.title}
subtitle={proj.subtitle}
desc={proj.desc}
itemKey={key} // just the key of the object
/>
)}
</div>
The thing is that the key of each object is the identifier in firebase and the value is the object with the data you need, but that object doesn't have a key property, therefore it was evaluated to null.

React-jsx syntax error when putting jsx into a class components method

In a React class component, I'm writing a method that builds a <select> dropdown and Babel is giving an error. The error is caused by the first <select> tag not being closed. I can't figure out the correct react-jsx syntax for this.
In the code below, I'm trying to encapsulate the code that creates the <select> into another method in the component class. If I have the function just build the options and put the <select> and </select> around that function (up in the render method) it works, but I want to put the selected value into the <select> tag instead of using the if statement, plus keep it all together.
What's the proper syntax for this?
Thanks...
The code with comments pointing to the problem line:
render()
{
function buildQtyOptions(isAvailable, qty)
{
var opts = [];
if(!isAvailable)
{
opts.push(<td></td>);
return opts;
}
{/* Uncommenting this next line causes the error because the select is unclosed */}
{/* opts.push(<select>) */}
for (var i=1; i < 11; i++)
{
if(i === parseInt(qty))
{
opts.push(<option value={i} selected>{i}</option>);
}
else
{
opts.push(<option value={i}>{i}</option>);
}
}
{/* opts.push(</select>); */}
return opts;
}
{/* Other methods omitted here */}
return(
<tr>
<td>{getImage(this.props.available)}</td>
<td>{this.props.name}</td>
<td>
{/* I want to move this and the closing select into the buildQtyOptions method */}
{/*<select> */}
{ buildQtyOptions(this.props.available, this.props.years) }
{/*</select> */}
</td>
</tr>);
JSX is not really markup, it's syntactic sugar for function calls.
<select></select>
becomes
React.createElement('select');
Maybe this helps to understand why you cannot really separate opening and closing "tags". You could consider them to be equivalent to parenthesis I guess.
Instead you can do
return <select>{opts}</select>;
(which translates to return React.createElement('select', null, opts);).
Try doing it all at once using map instead of using a for-loop, like this:
opts.push(
<select>
{ [...Array(10).keys()].map(x => x + 1).map((i) => {
if (i == parseInt(qty)) {
return <option value={i} key={i} selected>{i}</option>;
} else {
return <option value={i} key={i}>{i}</option>;
}
});
}
</select>
);
The [...Array(10).keys()] trick to generate a range of numbers is from this answer. The first map simply offsets the range so that you get numbers from 1 to 10 instead of from 0 to 9, and the second one actually generates the <option> components. This works because it is valid to pass the children of your component all at once as an array.
The reason you can't do this the way you want is that each closed pair of JSX tags gets translated into a React.createElement call, so if you don't close the JSX tag the JSX compiler has no idea when to call React.createElement.

Conditionally render list with Higher Order Component

My app has a feature toggle functionality that tells my UI whether or not a piece of UI should be rendered. I would like to create a Higher Order Component to conditionally render these types of components. In one scenario, I'm trying to conditionally render a list, but I'm running into this error:
ConditionalRender(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
This makes sense since I just am trying to render the children of this component. Here's what I've got so far:
https://jsfiddle.net/fmpeyton/cykmyabL/
var settings = { showHello: true, showGoodbye: false};
function ConditionalRender (props) {
var output = null;
if(props.shouldRender) output = props.children;
console.log(output);
// return (<li>{output}</li>); // This works, but isn't desired html structure
return ({output});
}
function App (props) {
return (
<ul>
<ConditionalRender shouldRender={props.settings.showHello}>
<li>Hello!</li>
</ConditionalRender>
<ConditionalRender shouldRender={props.settings.showGoodbye}>
<li>Goodbye...</li>
</ConditionalRender>
</ul>
);
}
ReactDOM.render(
<App settings={settings} />,
document.getElementById('container')
);
If I can help it, I would just like to render the children without any additional logic.This HOC would also handle more complex children down the line. Something like this:
<ConditionalRender shouldRender={props.settings.showHello}>
<div>
<p> blah blah blah</p>
<table>
<!-- ... -->
</table>
</div>
</ConditionalRender>
Any ideas?
Try this:
function App(props) {
return (
<ul>
{props.settings.showHello && <li>Hello!</li>}
{props.settings.showGoodbye && <li>Goodbye...</li>}
</ul>
);
}
P.S. Your code doesn't work because of this line:
return ({output});
Assuming you have es2015 support, it would be treated as object property shorthand. So it's the same as:
return {output: output};
which is not what React expects to get from the render method.
You could try this:
function ConditionalRender(props) {
if (props.shouldRender) {
// Make sure we have only a single child
return React.Children.only(props.children);
} else {
return null;
}
}
But this is not the simplest way. See more here.
P.P.S. Your ConditionalRender component is not what is called Higher-Order Component. According to the docs, HOC is a function that takes a component and returns a new component.

React Typescript: return array of Jsx elements

I'm trying React and TypeScript, but there's little information.
I have this situation in JavaScript:
render: function() {
var stars = [];
for (var idx = 1; idx <= this.state.max; idx++) {
var fill = idx <= this.props.data.score;
var hover = idx <= this.state.hoverIndex;
stars.push(<RatingStar fill={fill} index={idx} data={this.props.data} hoverFill={hover} hover={this.hoverStar} leave={this.leaveStar} />);
}
return stars;
}
This is easy in plain JavaScript. I simply return an array of elements. But in TypeScript this code gives me an error because render() returns a single JSX element, not an array. If I change the return type to array of JSX element, the error is class doesn't implement React.component... So any idea?
Update
This will work React 16 and latest TypeScript definitions. The below answer is preserved for people on React 15 and below 🌹
Old Answer
render() return a single JSX element not an array
This is TypeScript actually helping you. you must return a single element from the render.
Fix
Consider wrapping the output in a div. This might break your css so you need to think about reorganizing that as well.
Alternatively don't create a component and just {callFoo()} in JSX instead of <Foo/>.

Resources