How to loop React Elements for a specific amount of times? - javascript

I am trying to make a looper component so that I can loop any of its children for a specific amount of time.
How can I do that?
// Looper
function Looper({ children, array }) {
return (
<div>
{array.map((item) => (
<div key={item}>{children}</div>
))}
</div>
);
}
// It works, but it needs a dummy array that I don't want.
<Looper array={[1, 2, 3, 4, 5]}>
<span>Hello Guys..</span>
</Looper>

You can create an array of incrementing numbers on the fly using [...Array(times).keys()], like so:
// Looper
function Looper({ children, times }) {
const keys = [...Array(times).keys()];
return (
<div>
{keys.map((item) => (
<div key={item}>{children}</div>
))}
</div>
);
}
<Looper times={5}>
<span>Hello Guys..</span>
</Looper>

replace your map function with a for loop and pass a count i:e number of times you want the looper to render, and use that index as a key for the child divs then push them into an array. Finally, return that array of child divs. Check out this link if you want to go with this approach Loop inside React JSX

Related

React - sort array of child components with state

Currently I'm working on a react project, but I'm seeing some unexpected behavior when sorting an array of stateful child components.
If I have a parent component
export function Parent(){
const [children, setChildren] = useState([
{name:'Orange',value:2},
{name:'Apple',value:1},
{name:'Melon',value:3}
])
var count = 0
function handleSort() {
var newChildren=[...children]
newChildren.sort((a,b)=>{return a.value-b.value})
setChildren(newChildren)
}
return (
<div>
<button onClick={handleSort}>Sort</button>
{children.map((child) => {
count++
return(<ChildComp key={count} details={child}/>)
})}
</div>
)
}
And a child component
function ChildComp(props){
const[intCount,setIntCount] = useState(0)
function handleCount(){
setIntCount(intCount+1)
}
return (
<div>
<p>{props.details.name}</p>
<button onClick={handleCount}>{intCount}</button>
</div>
)
}
When the page first renders everything looks great, three divs render with a button showing the number of times it was clicked and the prop name as it was declared in the array. I've noticed that when I sort, it sorts the props being passed to the child components which then rerender, but the intCount state of the child component stays tied to the original location and is not sorted. is there any way to keep the state coupled with the array element through the sort while still maintaining state data at the child level, or is the only way to accomplish this to raise the state up to the parent component and pass a callback or dispatch to the child to update it?
The count is not is not sorted. It just got updated when you sorted.
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity
Every time you sort, key stay the same, as you use count.
Try using value as key
export function Parent(){
// ....
return (
<div>
<button onClick={handleSort}>Sort</button>
{children.map(child => {
return <ChildComp key={child.value} details={child}/> // key is important
})}
</div>
)
}
More info: https://reactjs.org/docs/lists-and-keys.html#keys

React.js: How to filter JSX element array on custom attribute?

I am starting with a simple array of JSX elements:
const jsxArray = dataItems.map(item => (
<div>
<Header>{item.title}</Header>
<Paragraph>{item.body}</Paragraph>
<Paragraph customAttribute={item.isActive} >{item.tags}</Paragraph>
</div>
))
Inside render, or rather return since I use functional components for everything now, I'd like to filter for JSX elements where the isActive attribute was tagged true.
return (
{jsxArray
.filter(jsxElement => // want to filter in JSX elements
// that are true for customAttribute keyed to `item.isActive`)
}
)
Is there any way to do it?
If there is not precisely a good way I am open to workarounds.
It is possible for me to simply filter the array at an earlier step. It would result in some extra code duplication though, since I would still need the array of unfiltered JSX elements elsewhere.
You don't filter the list after you render it. At that point it's just a tree of nodes that doesn't have much meaning anymore.
Instead you filter the items first, and then render only the items that pass your criteria.
const jsxArray = dataItems.filter(item => item.isActive).map(item => (
<div>
<h3>{item.title}</p>
<p>{item.body}</p>
<p customAttribute={item.isActive} >{item.tags}</p>
</div>
))
It is possible for me to simply filter the array at an earlier step. It would result in some extra code duplication though, since I would still need the array of unfiltered JSX elements elsewhere.
Not necessarily. When dealing with filtering like this myself I create two variables, one for the raw unfiltered list and one for the filtered items. Then whatever you're rendering can choose one or the other depending on its needs.
const [items, setItems] = useState([])
const filteredItems = items.filter(item => item.isActive)
return <>
<p>Total Items: ${items.length}</p>
<ItemList items={filteredItems} />
</>
Instead of accessing the jsx element properties (which I think it's either not possible or very difficult) I suggest you to act in this way:
Save the renderer function for items in an arrow function
const itemRenderer = item => (
<div>
<Header>{item.title}</Header>
<Paragraph>{item.body}</Paragraph>
<Paragraph customAttribute={item.isActive} >{item.tags}</Paragraph>
</div>
)
Save the filter function in an arrow function
const activeItems = item => item.isActive
Use them to filter and map
const jsxArray = dataItems.filter(activeItems).map(itemRenderer)
Use them to map only
const jsxArray = dataItems.filter(activeItems).map(itemRenderer)
Hope this helps!
Usually you would filter the plain data first and then render only the markup for the filtered elements as described in #Alex Wayne answer.
If you worry about duplication of the markup, that can be solved by extracting a component from it:
const Item = ({title, body, isActive, tags}) => (
<div>
<Header>{title}</Header>
<Paragraph>{body}</Paragraph>
<Paragraph customAttribute={isActive}>{tags}</Paragraph>
</div>
);
For rendering the filtered list you can then do:
{items.filter(item => item.isActive).map(item => <Item {...item} />)}
and for the unfiltered list:
{items.map(item => <Item {...item} />)}

How to loop through object with 2 arrays?

Trying to loop throught State passed by props on other component
state = {
question:[firstQ, secondQ, thirdQ],
tag:[[1,2,3],[4,6],[a,b,c,d]]
}
I want to render it on next Componet with Patter like:
FirstQ
[tag1]
SecondQ
[tag2]
ThirdQ
[tag3]
etc
I was trying lot of option but getting always something like
FirstQ
SecondQ
ThirdQ
[tag1]
[tag2]
[tag3]
EDIT:
Passing data to second Component with
question={this.state.question}
tag={this.state.tag}
EDIT2:
For now i made loops like this
{this.props.question.map((item,) => {
return (<span key={item}>{item}</span>)
})}
{this.props.tag.map((item) => {
return (<span>{item<span>)
})}
I trying to render this two arrays as pairs Question1 => Tag1 then underneath second Question2 = >tag2 etc.
Use the index of question to get matching tags
Something like:
{this.state.question.map((q,i)=>{
return (
<div>
<h4>{q}</h4>
Tags: {this.state.tag[i].join()}// map these to element you want instead of join()
</div>
)
})

Loop through array in react onclick

I have an array of objects. How can I loop through this array on click?
test = ['a', 'b', 'c', 'd']
{this.state.test.map(function(i){
return <span> {i} </span>
})}
I would normally loop through like this in react but this prints them all out at once. How can I display 'a' on the page and then the next time i click it, I display 'b' so on until the end of the array?
I need a function that can tell where I am in the array and display that on the page at the right moment
You could implement e.g. a counter variable and use Array#slice to show specified amount of elements inside the test array.
Codesandbox link
import React from "react";
export default class Hello extends React.Component {
state = {
test: ["a", "b", "c", "d"],
index: 0
};
handleClick = () => {
let i = this.state.index < this.state.test.length ? this.state.index += 1 : 0;
this.setState({ index: i });
};
render() {
return (
<div>
{this.state.test.slice(0, this.state.index).map(v => {
return (
<span>{v}</span>
);
})}
<button onClick={this.handleClick}>Click</button>
</div>
);
}
}
Edit: I was playing with it and actually I got even a better solution, which allows you to avoid re-looping Array#map on every render, with little help of hidden attribute. Also we are getting rid of the Array#slice function.
Improved codesandbox link
app.js
{this.state.test.map((v, i) => {
return (
<span hidden={i >= this.state.index}>{v}</span>
);
})}
And the Span.js component:
<span hidden={this.props.hidden}>
{this.props.v}
</span>
set initial index at your state first, then use that index as index array in span. next make some function to handle the increment, simply put it like this
const switchNinjaHandler = () => {
let looper = ++this.state.index % this.state.test.length;
setState({
...personsState, // WE SHOULD ALWAYS GIVE THE EXCACT INITIAL STATE VALUE, THIS IS WHERE SPREAD COMING HANDY
this.index : looper,
});
to display
return ( <div> {this.state.test[this.index]} </div> )

Find First Iteration of Map in React/Javascript

I'm mapping an object array in React and I want to find the first iteration of chosenFields array.
People Array:
[ { id:1, name:"Jim", occupation:"Programmer".........}
{id:2, name:"Sally", occupation:"Pet Sitter".......}]
I have another object array that is in charge of what fields are suppose to be displayed from the PeopleArray:
chosenFields Array:
[{label:"id", show:true}, {label:"name", show:true}, {label:"occupation", show:false}]
I want to know if there's a way that the code can recognize when it first iterates through chosenFields Array for each row of the People Array
renderSingleData(rowListData, currData){
//find which one is the first iteration to add a radiobox for each row
}
renderUserData(currRow){
return(
<div>
{chosenFields.map(this.renderChosenData.bind(this, currRow)}
</div>
)
}
render() {
return (
<div >
{PeopleData.map(this.renderUserData.bind(this))}
</div>
);
}
}
This is a very simplified version of the code. I need to add an iteration to a table via <td>. I'm currently using a variable in React and setting the state with a counter. I'm wondering if there's a more straightforward way to find iterations via the map function.
Take a look at the docs for Array.prototype.map()
The callback function's second argument is the index. If this is 0, then that is the first iteration of the callback. In your case, you may need to propagate that condition up a few callbacks. Something like...
renderSingleData(isFirst, rowListData, currData){
//find which one is the first iteration to add a radiobox for each row
}
renderUserData(currRow, currIdx){
return(
<div>
{chosenFields.map(this.renderChosenData.bind(this, currIdx === 0, currRow)}
</div>
)
}
render() {
return (
<div >
{PeopleData.map(this.renderUserData.bind(this))}
</div>
);
}

Categories

Resources