Creating a looped HTML element in React - reactjs

I'm relatively new to React, I've looked a lot of places and can't find a way to specifically handle this. I have checked previous answers.
I am trying to get my application to loop through an array and also print a statement along with the array.
var user = ["Kevin", "Kyle", "Kylian"];
var Hello = <h1>
Hello, {user}!
</h1>
class App extends Component {
render() {
for(var i=0;i<user.length;i++){
return Hello;
}
}
}
export default App;
Output:
Hello, KevinKyleKylian!
Expected Output:
Hello, Kevin!
Hello, Kyle!
Hello, Kylian!
As you can see, the loop for some reason doesn't continuously return the entire output and after the user iteration of {user} it just prints {user} until the array is ended. Why does this happen? How can I avoid this?

It's probably because of this bit:
var Hello = <h1>
Hello, {user}!
</h1>
In that case 'user' is referring to the whole array, not just a specific element of that array.
Generally, if you're building elements dynamically in React it's good to put that in a separate function rather than the render method, I feel it's a bit neater. So something like this:
getUsers() {
let userList = [];
for (let i=0; i<user.length; i++) {
userList.push(<div>Hello, {user[i]}</div>);
}
return userList;
}
render() {
return(
<div>
{this.getUsers()}
</div>
);
}

Related

React - how to render multiple buttons with a regular for loop?

Sorry if its very basic but:
when rendering multiple buttons (0-9) in an iteration - What is the difference btw map and for loop ? Why does the for loop only renders the first element (0) while map works fine? Why do I have to first push the buttons into an array and return that then (as seen on other examples) ? Can I use regular for loop and render buttons without pushing it into an arary?
Thanks!
import React from 'react';
const Keys = () => {
const renderKeys = () => {
//works fine
var arr = [1,2,3,4,5,6,7,8,9]
return arr.map((val) => {
return <button>{val}</button>
})
};
const renderKeys = () => {
//does not work
for (var i=0; i<10; i++) {
return <button>{i}</button>
}
};
return (
<div>
{renderKeys()}
</div>
)
};
When you call return inside a for-loop it stops executing the loop. That's why you only get back the first button.
However, calling return inside a .map() will not stop the loop from iterating. Instead you use return to explicitly define what you want to have in a new array.
Note that .map() creates a brand new array by using elements from an existing array. You are free to utilize those elements any way you want which makes it suitable for rendering JSX.
Example:
const numbers = [1, 2, 3, 4, 5]
const numbersMultipledByTwo = numbers.map((number) => {
return <div>{ number * 2 }</div>
})
Theoretically, you could accomplish the same effect using a for-loop but that will also require help from a second array.
Working code:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component{
getButtonsUsingMap = () => {
const array = [1, 2, 3 ,4, 5]
return array.map((number) => {
return <button>{number}</button>
})
}
getButtonsUsingForLoop = (num) => {
const array = []
for(var i = 1; i <= num; i++){
array.push(<button>{i}</button>)
}
return array
}
render(){
return(
<div>
<h4>Using .map()</h4>
{this.getButtonsUsingMap()}
<h4>using for-loop</h4>
{this.getButtonsUsingForLoop(5)}
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
In that getButtonsUsingForLoop function, you can see there are more explicit demands to make it work. First we need to satisfy the argument. Then initialize a new array. Then define a boundary for the loop. Iterate and push JSX to the empty-array. Then finally return that array. So the logic is not very succinct.
Whereas on the other-hand, a .map() essentially handles all of that. As long as you have a pre-existing array to iterate over (which 99% of the time you will be dealing with some sort of state or props-array.)
See sandbox: https://codesandbox.io/s/pensive-leftpad-lt5ml
What is the difference btw map and for loop?
.map() iterates an array (and arrays only) while a for loop could be lazily summarized as a more "general" loop mechanism that is independent of any specific data type.
Why does the for loop only renders the first element (0) while map works fine?
Because you're returning from the function in the first iteration of the for loop with
const renderKeys = () => {
//does not work
for (var i=0; i<10; i++) {
return <button>{i}</button> // this returns from the function you're in
}
};
.map() works fine because it returns a new array from iterating the input-array, e.g.:
const renderKeys = () => {
// works fine
var arr = [1,2,3,4,5,6,7,8,9]
return arr.map((val) => { // here you return the new array created by map
return <button>{val}</button>
});
};
Why do I have to first push the buttons into an array and return that then (as seen on other examples)?
basically to "imitate" what map does, e.g. creating a new array from the iteration.
Can I use regular for loop and render buttons without pushing it into an array?
Directly return you mean? I don't think it's possible, but maybe somebody else does know a way!
Why I think it's not possible?
return (
<div>
{renderKeys()}
</div>
)
in JSX you return a function and you can't pass a for loop as a function argument directly, e.g. this:
return (
<div>
for (var i=0; i<10; i++) {
<button>{i}</button>
}
</div>
)
would most likely give you a syntax error...

How to render dynamic components onClick

My code creates dynamic components/elements based on size of list of entities (in cache/memory). How can I modify this so that the onClick on line 32 (fab-click) adds yes another entity to the list. I ommitted the MainMenuElement class so lets assume it works. I think this is a problem of not knowing how to "think in react". Must I use react's state to achieve this, or is there a cleaner way?
I am actually adapting this from an HTML5/CSS3 app which used and am finding this to be much harder than just appending children from anywhere/any-time like with templates. Help.
createMainMenuElement(conversation){
return <MainMenuElement conversation = {conversation} key ={conversation.key} />
}
createMainMenuElements(conversations) {
return conversations.map(this.createMainMenuElement)
}
generateData = function(){
let usernames = ["tony","john","doe","test", "bruce"]
let data = [];
for(let i = 0; i < 5; i++){
let temp = {key: i.toString(), username: usernames[i], timestamp: "4:30", subtitle: "The quick brown fox jumps over the lazy dog"}
data.push(temp)
this.mainMenuStack+=1;
}
return data;
};
handleFabClick() {
console.log("test")
let temp = {key:this.mainMenuStack.toString(), username: "Baby", timestamp: "12:30", subtitle: "The quick red cat jumps over the yellow dog"};
this.createMainMenuElement(temp);
};
render(){
return(
<div className={cx('mainMenu')}>
<div className={cx('mainMenu__element','mainMenu__element--pseudo')} onClick={this.handleFabClick}>
<div className={cx('mainMenu__element__icon fab')} id="fab">
<div className={cx('fab__icon')}>+</div>
</div>
<div className={cx('mainMenu__element__textWrapper')}>
<div className={cx('mainMenu__element__title')}>New Conversation</div>
</div>
</div>
{this.createMainMenuElements(this.generateData())} //WORKS ON LOAD
//WANT TO RENDER/APPEND DYNAMIC COMPONENTS HERE
</div>
)
};
}
You're thinking about the DOM, when you need to think about the data. In React, the DOM is purely a function of your data.
You'll need to store the dynamically created data, let's use an array
constructor(props){
super(props)
this.state = {
elements:[]
}
}
Then render the data. elements is empty for now, that's fine. But we know that eventually, the user will create data dynamically. The render function already handles that!
render(){
return (
<div>
//other code
{this.state.elements.map(this.createMainMenuElement)}
</div>
)
}
Now let's add the data.
handleFabClick() {
let temp = {key:this.mainMenuStack.toString(), username: "Baby", timestamp: "12:30", subtitle: "The quick red cat jumps over the yellow dog"};
this.setState({
elements: [...this.state.elements, temp]
})
};
We've now changed the state of the component, which causes it to rerender, which will display the new data. No DOM operations needed!
My code does not translate directly to your question, i'm merely showing you the React fundamentals. You said that you want to add elements to an existing list, so it looks like elements needs to contain ["tony","john","doe","test", "bruce"] by default. I hope you get the point though.

Simple for loop in react This code gives Errors at for loop

This gives Error at for loop
let contact=[{name:"Mithun"},{name:"Keerthana"},{name:"Jayendara"},{name:"Shivani"}]
for (i=0;i<list;i++)
{
<h1>{content[0].name}</h1>
}
You need to use contact.length rather than list in the for loop. You also need to use contact[i] rather than content[0].
for (i = 0; i < contact.length; i++) {
<h1>{contact[i].name}</h1>
}
If you are using TSX (TypeScript + React), you can use the map function to make this easier.
return contact.map(c => <h1>{c.name}</h1>);
Suggest you a few things
In your question you are looping over list rather than that you should be looping over contacts
As I understand you wish to craete a JSX element from the contact objects. So you need to push it into an array and then render it like
Code:
let contact=[{name:"Mithun"},{name:"Keerthana"},{name:"Jayendara"},{name:"Shivani"}]
var content = [];
for (i=0;i<contact;i++)
{
content.push(<h1>{contact[i].name}</h1>);
}
and when you want to render this in your render function you will do something like
return (
<div>{content}</div>
)
However since you are using react you should be using map function which are more convient and easy to use
Your code will look like
render() {
return(
<div>
{contacts.map(function(item) {
return (<h1>{item.name}</h1>)
})}
</div>
)
}

ReactJS parent/child list items not rendering properly after an item is removed

Example: https://jsfiddle.net/wbellman/ghuw2ers/6/
In an application I am working on, I have a parent container (List, in my example) that contains a list of children (Hero, in my example). The list is governed by an outside object. For simplicity I declared the object directly in the JS. (In my real application the data store is properly namespaced and so forth.)
The problem I have is in the list I have three elements, if I remove an item from the middle, the rendered list appears to remove the last element. However the outside object reflects the proper list.
For example:
My list has the elements: cap, thor, hulk
If you remove "thor", "cap" and "thor" are rendered
The heroList reflects "cap" and "hulk" as it should
I am relatively new to ReactJs, so there is a good chance my premise is fundamentally flawed.
Note: The example reflects a much more complex application. It's structured similarly for purposes of demonstration. I am aware you could make a single component, but it would not be practical in the actual app.
Any help would be appreciated.
Here is the code from JSFiddle:
var heroList = [
{ name: "cap" },
{ name: "thor"},
{ name: "hulk"}
];
var List = React.createClass({
getInitialState() {
console.log("heros", heroList);
return {
heros: heroList
};
},
onChange(e){
this.setState({heros: heroList});
},
removeHero(i,heros){
var hero = heros[i];
console.log("removing hero...", hero);
heroList = _.filter(heroList, function(h){ return h.name !== hero.name;});
this.setState({heros:heroList});
},
render() {
var heros = this.state.heros;
var createHero = (hero,index) => {
return <Hero hero={hero} key={index} onRemove={this.removeHero.bind(this,index,heros)}/>;
};
console.log("list", heros);
return (
<ul>
{heros.map(createHero)}
</ul>
)
}
});
var Hero = React.createClass({
getInitialState() {
return {
hero: this.props.hero
}
},
render() {
var hero = this.state.hero;
return (
<li>Hello {hero.name} | <button type="button" onClick={this.props.onRemove}>-</button></li>
);
}
});
ReactDOM.render(
<List />,
document.getElementById('container')
);
Additional: I was having problems copying the code from JSFiddle, anything I broke by accident should work in the JSFiddle listed at the top of this question.
Edit:
Based on the commentary from madox2, nicole, nuway and Damien Leroux, here's what I ended up doing:
https://jsfiddle.net/wbellman/ghuw2ers/10/
I wish there was a way to give everyone credit, you were all a big help.
Changing your Hero class to this fixed the issue of displaying the wrong hero name for me:
var Hero = React.createClass({
render() {
return (
<li>Hello {this.props.hero.name} | <button type="button" onClick={this.props.onRemove}>-</button></li>
);
}
});
i.e. I removed the local state from the class and used the prop directly.
Generally speaking, try to use the local store only when you really need it. Try to think of your components as stateless, i.e. they get something through the props and display it, that's it.
Along these lines, you should consider passing the hero list through the props to your List component as well.
if you really have problems with managing your data you should use Flux or Redux.
in this code:
heroList = _.filter(heroList, function(h){ return h.name !== hero.name;});
i just dont get why you filer the heroList instead of this.state.heros? every time you add or remove a hero, the heroList in your current scope shouldnt be kept in state? the global heroList is just the initial state.
The problem is with the keys used. Since the key is taken from the index, that key has already been used and thus the hero with that key is shown.
change it to key={Math.random() * 100} and it will work

Reactjs How to insert react component into string and then render

How to create a reactjs component that will render the props data with another component.
for example I have a sentence say "Hello guys this is {{name}}. How are you.". Now I want to replace the name with the reactjs component.
when I try to replace the name with the component it shows as [object object].
First Edit:
var sentence = "Hello guys this is {{name}}. How are you.";
var tag_values = {'name': 'any Name'}
TagBox will take sentence and tag_value as props and replace the tags with the Tag component. and render it
var TagBox = React.createClass({
render: function(){
// replacing the tags with Tag component
this.props.sentence = this.props.sentence.replace(tags_values['name'], <Tag \>)
return(
<div>
{this.props.sentence} //Issue: This will Print as "Hello guys this is [Object Object]. How are you."
// But this should print as "This will Print as Hello guys this is any Name. How are you."
// After clicking on "any Name" it should be replaced with input.
</div>
);
}
})
Tag Component will replace the tag with input box on double click. and again replace input box with data on enter.
This can be done using state.
var Tag = React.createClass({})
Okay, so assuming that's a string you have as input, you need to create an array.
var parts = str.split(/\{\{|\}\}/g);
// => ["Hello guys this is ", "name", ". How are you."]
The odd items are literal strings, and the even parts are the stuff between the brackets.
Now we'll create a helper function called mapAlternate. Which takes a function to call for odd elements, and a function to call for even elements in our array.
function mapAlternate(array, fn1, fn2, thisArg) {
var fn = fn1, output = [];
for (var i=0; i<array.length; i++){
output[i] = fn.call(thisArg, array[i], i, array);
// toggle between the two functions
fn = fn === fn1 ? fn2 : fn1;
}
return output;
}
Now we can do something like this in our component:
render: function(){
var parts = str.split(/\{\{|\}\}/g);
// render the values in <strong> tags
var children = mapAlternate(parts,
function(x){ return <span>{x}</span>; },
function(x){ return <strong>{x}</strong> });
return <div>{children}</div>;
}
Which gives us: "Hello guys this is name. How are you."
Have you heard of React String Replace ?
Here is a stateless component example:
import replace from 'react-string-replace';
const reg = /\{([a-z|A-Z|0-9|\.]+)\}/g;
const OutputComponent = props => {
var str = 'Hello {name}, this is a "Super" component: {Super}';
var output = replace(str, reg, prop => props.replacements[prop]);
return <div>{output}</div>;
}
// later
import Super from './Super.jsx';
const obj = {
Super: <Super />,
name: 'John'
}
return <OutputComponent replacements={obj} />;
I just fixed this issue with react-jsx-parser
Your Example would be:
import JsxParser from 'react-jsx-parser'
export default class TagBox extends React.Component {
render() {
const sentence = "Hello guys this is <Tag>name</Tag>. How are you." // simply include the component in your string
return(
<JsxParser components={{ Tag }} jsx={ sentence } /> // identify the component in your string to inject
)
}
}
Nothing from above doesn't worked for me unfortunately. Here is a useful stable solution regexify-string (npm)
npm install --save regexify-string
Works like a charm
regexifyString({
pattern: /\[.*?\]/gim,
decorator: (match, index) => {
return (
<Link
to={SOME_ROUTE}
onClick={onClick}
>
{match}
</Link>
);
},
input: 'Some initial string with [link]',
});

Resources