I have an array, for which a component (<span>Delete</span> in my case) needs to be shown only if the array length is greater than 2 and also, not show in first two array elements but show in other elements.
Example:
array = ["One","Two","Three","Four"]
Show <span>Delete</span> in "Three" and "Four" as the array is greater than length 2.
In the above case, even if the array is greater than length 2 don't show in first two elements ( "One" and "Two" ).
Code:
const App = () => {
const [element, setElement] = useState(["element"]);
return (
<>
{element.map((e) => (
<div>
<span>{e}</span>
{element.length > 2 && <span style={{color:"red"}}>{" "}Delete</span>}
</div>
))}
<button onClick={() => setElement((element) => [...element, "element"])}>Add</button>
</>
);
};
The above code shows "Delete" text in all the elements
instead should show only for elements after the 2nd index.
CodeSandbox: https://codesandbox.io/s/basic-antd-4-16-9-forked-2w3lg5?file=/index.js:98-478
Use the index of the element you're iterating over (the second argument in the .map callback) to check if the index is 2 or more - if so, you can show the delete, otherwise don't show it.
const App = () => {
const [elements, setElements] = useState(["element"]);
return (
<>
{elements.map((e, i) => (
<div>
<span>{e}</span>
{elements.length > 2 && i >= 2 && <span style={{color:"red"}}>{" "}Delete</span>}
</div>
))}
(Because the state is an array, making its name plural makes a lot more sense than a singular element, which sounds like an array item)
You also need to check if the index of the current item is greater than 1.
const App = () => {
const [element, setElement] = React.useState(["element"]);
return (
<React.Fragment>
{element.map((e, i) => (
<div>
<span>{e}</span>
{i > 1 && element.length > 2 && <span style={{ color: "red" }}>Delete</span>}
</div>
))}
<button onClick={() => setElement((element) => [...element, "element"])}>
Add
</button>
</React.Fragment>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
You can do something like this
{element.length > 2 && element.map((ele,index) => {
if(index > 1){
return <p>{ele}</p>
}
else{
return <></>
}
})}
You need to access the current index of the current element (e), so include that in the parameter list of map().
Secondly, the condition to check if the index (idx) is greater than or equal to (>=) 2.
const App = () => {
const [element, setElement] = useState(["element"]);
return (
<>
{element.map((e, idx) => (
<div>
<span>{e}</span>
{element.length && idx >= 2 && <span style={{color:"red"}}>{" "}Delete</span>}
</div>
))}
<button onClick={() => setElement((element) => [...element, "element"])}>Add</button>
</>
);
};
Related
SOLUTION: Update the key value for the input element to refresh the default value => content of the input element. Deleting an element from the array DID work. Thanks for your help!
src: https://thewebdev.info/2022/05/12/how-to-fix-react-input-defaultvalue-doesnt-update-with-state-with-javascript/#:~:text=state%20with%20JavaScript%3F-,To%20fix%20React%20input%20defaultValue%20doesn't%20update%20with%20state,default%20value%20of%20the%20input.
I got an useState array in my code which represents a lisst of students:
const [students, setStudents] = useState([""]);
This array gets mapped to student elements:
{students.map((student, index) => <Student setStudents={setStudents} students={students} id={index} key={index} content={student} />)} I also got an AddStudent element which adds students to the array.
function AddStudent(props) {
const {setStudents} = props;
return (
<button className="change-student add-student" onClick={() => {
setStudents((students) => [...students, ""])
}}>
+
</button>
);
}
The RemoveStudent component is supposed to remove a student by its index in the array. I've tried many different ways but none worked correctly. How can I get it to work? Here is my code:
function RemoveStudent(props) {
const {students, setStudents, id} = props;
return (
<button className="change-student remove-student" onClick={() => {
let data = students;
if(id > -1) {
data.splice(id, 1);
}
console.log(data)
// setStudents(data)
// alternative:
// setStudents(students.filter(index => index !== id)); // removes the last element in the list
// doesn't work properly
}}>
-
</button>
)
}
Thanks for your help!
2 things should be noted here:
While updating react state arrays, use methods that return a new array (map, filter, slice, concat),
rather than ones that modify the existing array (splice, push, pop, sort).
While updating React state using its previous value, the callback argument should be used for the state setter. Otherwise you may get stale values. (See React docs).
if(id > -1) {
setStudents(students=> students.filter((s,i)=>(i != id)))
}
Consult this article, for a complete reference about how to update React state arrays.
You need to copy the students array first and then try removing the student by index. I assume by id you mean index at which to remove the student. Then you can try something like:
function RemoveStudent(props) {
const {students, setStudents, id} = props;
return (
<button
className="change-student remove-student"
onClick={() => {
if(id > -1) {
const data = [...students]; // making a copy
data.splice(id, 1); // removing at index id
console.log(data)
setStudents(data)
}
}}
>
-
</button>
)
}
With array.filter() you have a mistake in how you pass callback to filter() method. Please try the following:
setStudents(students.filter((,index) => index !== id));
Notice the index is second param of the callback so I used a , before index.
After #Irfanullah Jan 's answer you should make sure how you show the student.
Here is the simple example:
const [students, setStudents] = useState([1, 2, 3]);
return (
<div>
{students.map((student, index) => {
return <div>{student}</div>; // show the value not the index
})}
<button
onClick={() => {
let id = 1;
const copy = [...students];
copy.splice(id, 1)
console.log(copy)
setStudents(copy);
}}
>
-
</button>
</div>
);
The code above will delete the student of "index==1"
Is there any way I can include the wrapping div's FilterListContainer, FilterListScroll and FilterList in the map itself?
So if there is something to map, it will add the parent div's. If not it wont.
<FilterListContainer>
<FilterListScroll>
<FilterList>
{Object.keys(this.props.body_search_filter)
.map((k) => (
<SidebarFilter
key={k}
type={k}
filter={this.props.body_search_filter[k]}
handleChange={this.handleFilterChange}
/>
))
.filter(
(i) =>
i.props.filter.list.length > 0 &&
((!i.props.filter.optional && !i.props.filter.hidden) ||
(i.props.filter.list.length !== 1 &&
!i.props.filter.list[0].disabled))
</FilterList>
</FilterListScroll>
</FilterListContainer>
You'll be able to use short-circuiting of logical operators here:
{Object.keys(this.props.body_search_filter).length && (
<FilterListContainer>
<FilterListScroll>
<FilterList>
{Object.keys(this.props.body_search_filter)
.map((k) => (
<SidebarFilter
key={k}
type={k}
filter={this.props.body_search_filter[k]}
handleChange={this.handleFilterChange}
/>
))
.filter(
(i) =>
i.props.filter.list.length > 0 &&
((!i.props.filter.optional && !i.props.filter.hidden) ||
(i.props.filter.list.length !== 1 &&
!i.props.filter.list[0].disabled))
</FilterList>
</FilterListScroll>
</FilterListContainer>
)}
But you might want to filter the list, then check if the filtered list has any elements instead:
const filtered = Object.keys(this.props.body_search_filter).filter((k) => {
const f = this.props.body_search_filter[k];
return f.list.length > 0 &&
((!f.optional && !f.hidden) ||
(f.list.length !== 1 && !f.list[0].disabled))
});
// ...
// then use 'filtered' instead
{filtered.length && (
<FilterListContainer>
<FilterListScroll>
<FilterList>
{filtered.map((k) => (
<SidebarFilter
key={k}
type={k}
filter={this.props.body_search_filter[k]}
handleChange={this.handleFilterChange}
/>
))}
</FilterList>
</FilterListScroll>
</FilterListContainer>
)}
Moving my comment to an answer to add a snippet
You could do the map before the return. Then you can 'check' if the map has some content, if so, use it, otherwise, don't render (or use fallback)
Please see comments in code
const { useState } = React;
const Example = () => {
// Demo data
const [data, setData] = useState([ 'foo', 'bar' ])
// Filter + Mapping logic
const mapped = data
.filter(d => d !== 'foobar')
.map(d => <li>{d.toUpperCase()}</li>);
// Content based on map, use it, or render 'null'
// The wrapped 'ul' is OP's FilterListContainer as an example
const content = !mapped.length ? null :
(
<div>
<ul>
{mapped}
</ul>
</div>
);
// Render
return content;
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I want to display the Project Name and other indexes from filteredResults, how can I map it and its indexes? Or parsing?
I put it on onClick event:
function filter() {
var filteredResults = projectData.filter((f => f.ProjectName.includes(filterKeyword) && (f => f.PartitionKey.includes(nameArea))));
console.log(filteredResults);
};
return:
<Stack direction="row" id="projectResults">
<Masonry columns={2} spacing={2}>
{!!projectData && (
projectData.map((card, index) => (
<MasonryItem key={card.RowKey}>
...
</MasonryItem>
))
)}
</Masonry>
</Stack>
Your filter callback is not valid.
Try changing it to this:
var filteredResults = projectData.filter(f => f.ProjectName.includes(filterKeyword) && f.PartitionKey.includes(nameArea));
I have react component that gets index from map function, I'm trying to open div of row tag when my index is even and close it when my index is odd
render() {
return (
{this.props.index%2===0 && (<div className="row mt-1">)} //new row
<div className="col-1">{this.props.title}</div>
<div className="col-5">
<ProgressBar
variant={this.props.color}
now={this.props.now}
max={this.props.max}
label={this.props.label}
/>
</div>
{this.props.index%2===1 && (</div>)} //end of the row
);
}
this code in not compile:
enter image description here
the point is that every row contain two ProgressBar. What is the right way to do it?
You need to deal with whole elements are once, not tags.
This is easier if you break it up into functions.
You can use splice on an array to grab two items at a time.
e.g.
function createRow(elements) {
return (
<div>
{elements.map(createProgressBar)}
</div>
);
}
function createProgressBar(element) {
return (
<div>{element.index}</div>
);
}
function render() {
// ...
const rows = [];
while (myArray.length) {
const thisRow = myArray.splice(0,2);
rows.push(createRow(thisRow));
}
return rows;
}
You should modify the shape of your array into something like this before trying to render it.
[1,2,3,4] => [[1,2],[3,4]]
that way it would be easier for you to wrap it inside div.
see the live demo
Code to transform your flat array into nested array:
const list = [1, 2, 3, 4];
const [state, setState] = React.useState([]);
React.useEffect(() => {
let res = [];
for (let i = 0; i < list.length; i += 2) {
res.push([list[i], list[i + 1]]);
}
setState(res);
}, []);
Render logic:
{state.map(item => {
return (
<div style={{ border: "1px solid black", padding: "1em" }}>
{item.map(i => i)}
</div>
);
})}
I have a Search component that outputs values from an array to a ResultItem child component, every child component has a button with a onClick property on it. I bound a function to the button to get the value of an array item that I clicked.
What I have working is that every time I clicked on every single ResultItem button I get values of 0,1,2,3,4,5,6 individually which is perfect but I dont need the array indexes I need the values of those indexes
class ResultItem extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick
}
handleClick(index) {
// index = this.props.applications[0]
// index = this.props.applications.map(obj => obj.videoId[0])
console.log('this click', index)
}
render() {
// console.log ('myProps', this.props.applications[0]);
const {applications} = this.props;
return (
<div>
{
applications.map((app, k) => {
return (
<Card key={k} style={styles.appCard}>
<CardMedia style={styles.appMedia}>
<div>
<Drafts color={white} style={styles.iconer}/>
</div>
</CardMedia>
<CardTitle key={k} title={app.videoId} subtitle="Application"/>
{/* <div key={k}><h3>{app}</h3></div> */}
<CardText>
<div>
<div>Status:
<b>test</b>
</div>
</div>
</CardText>
<FloatingActionButton
style={styles.addButton}
backgroundColor={'#CC0000'}
onClick={this.handleClick.bind(this, k)}
>
<ContentAdd/>
</FloatingActionButton>
</Card>
)
})
}
</div>
);
}
}
What I've tried so far:
if I use:
index = this.props.applications[0]
I get the first value of the array on ALL buttons I click on and
If I use:
index = this.props.applications.map(obj => obj.videoId[0])
I get the first letter of every single item of the array inside another array on every click, Is there any way I can get the value of the element I've clicked on , if so how?
When you map over an array you provide a function where the first argument is the current item, and the second one is the current index (of that item).
someArray.map((currentItem, currentIndex) => /* do stuff */ )
If you just care about the item, then there is no need to involve the index. You could just pass the current item directly to handleClick.
render() {
const {applications} = this.props;
return (
<div>
{
applications.map((item) => {
<FloatingActionButton
style={styles.addButton}
backgroundColor={'#CC0000'}
onClick={ this.handleClick.bind(this, item) }
>
</Card>
)
})
}
</div>
);
handleClick would then deal directly with the item, not the index. If you still want to use indexes, perhaps because that's nice to have if you need to manipulate the array at some later stage, then the index (second argument), let's call it index, could be passed to handleClick as before, and you would use that index to find the relevant item with
const clickedItem = this.props.applications[index]
index = this.props.applications[index]
or
index = this.props.applications[index].videoid