React - show first item in map based on condition - reactjs

I have StackBlitz Demo here
Simple question is it possible to show the first item in a map based on a conditional.
So in my example how could I just output the first item '1' if 'first' is true
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
const list = ["1", "2", "3", "4"];
const first = true
return (
<div>
<ul>
{list.map(item => (
<li>{item}</li>
))}
</ul>
</div>
);
};
render(<App />, document.getElementById("root"));

This should work. If first is true it will only show the first element else it will show all.
const App = () => {
const list = ["1", "2", "3", "4"];
const first = false;
return (
<div>
<ul>
{first ? (
<li>{list[0]}</li>
) : (
list.map((item, index) => {
return <li>{item}</li>;
})
)}
</ul>
</div>
);
};

stackblitz
{list.map((item, i) => {
if (i === 0 && item) return <li>{item}</li>;
})}

<ul>
{
list.map((item,index) => (
(index === 0 && first) ? <li>{item}</li> : null
))
}
</ul>
Try the above code.

{list.map((item, index) => (
<li>{first ? (index === 0 ? item : null) : item}</li>
))}

Related

useEffect is not rendering an array on DOM

I am populating an array with dummy data and once it is done, using useEffect, i'm trying to delete the the element on first index and update it on the DOM. The array is being updated on console but the changes doesn't reflect on DOM.
import './App.css';
import { useState,useEffect } from 'react';
import {faker} from '#faker-js/faker'
function App() {
var catsarray = []
const [cats, changecat] = useState(()=>{
for (let index = 0; index < 10; index++) {
catsarray.push(faker.animal.cetacean())
}
return catsarray
})
useEffect(()=>{
},[cats])
const removecat = () => {
cats.shift()
changecat(cats)
}
return (
<div className="App">
<h1> List of cats </h1>
<div className='wrapper'>
<ul>
<>
{catsarray.length > 2 ?
cats.map((title, index)=>(
<li key={index}> {index} : {title} </li>
)) : 'Array is empty' }
</>
</ul>
</div>
<button title='button' onClick={removecat}> Click me </button>
</div>
);
}
export default App;
The reason is like #Jacob Smit said:
React uses referential comparisons to determine whether or not a value has changed. If you mutate (change) the state and provide it back to the setState function react sees the same value as it already has stored and thinks there is no operation to be carried out
So you need to use a different refernece when mutating a state
for example
// assign a new array
let tempCats = [...cats]
// operate this new array
tempCats.shift()
// set the new array to state
changecat(tempCats)
Or like #Jacob Smit's suggestion
changecat(cats => cats.filter((_, i) => i !== 0))
a example from your question with a little modify
import React from "react"
import { useState, useEffect } from 'react';
function App() {
var faker = ["steve", "john", "cat", "dog", "alex", "big"]
var catsarray = []
const [cats, changecat] = useState(() => {
for (let index = 0; index < faker.length; index++) {
catsarray.push(faker[index])
}
return catsarray
})
useEffect(() => {
}, [cats])
const removecat = () => {
let tempCats = [...cats]
tempCats.shift()
console.log(tempCats)
changecat(tempCats)
// or #Jacob Smit's suggestion
//changecat(cats => cats.filter((_, i) => i !== 0))
}
return (
<div>
<h1> List of cats </h1>
<div>
<ul>
<>
{cats.length > 2 ?
cats.map((title, index) => (
<li key={index}> {index} : {title} </li>
)) : 'Array is empty'}
</>
</ul>
</div>
<button title='button' onClick={removecat}> Click me </button>
</div>
);
}
export default App;
a code snippet display
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function App() {
var faker = ["steve", "john", "cat", "dog", "alex", "big"]
var catsarray = []
const [cats, changecat] = React.useState(() => {
for (let index = 0; index < faker.length; index++) {
catsarray.push(faker[index])
}
return catsarray
})
React.useEffect(() => {
}, [cats])
const removecat = () => {
let tempCats = [...cats]
tempCats.shift()
console.log(tempCats)
changecat(cats => cats.filter((_, i) => i !== 0))
}
return (
<div>
<h1> List of cats </h1>
<div>
<ul>
{cats.length > 2 ?
cats.map((title, index) => (
<li key={index}> {index} : {title} </li>
)) : 'Array is empty'}
</ul>
</div>
<button title='button' onClick={removecat}> Click me </button>
</div>
);
}
</script>
<script type="text/babel">
ReactDOM.render(
<App></App>
, document.getElementById("root"));
</script>

How to create a nested accordion in reactjs

Intro -
I'm trying to show JSON data in a Accordion. So I used react-sanfona (github) to build that. I'm trying to call getComponent function recursively so that I can check if it is an array or object if it is array I'm calling same function. better show you the pogress so far.
Problem - I'm getting [object Object] at the second level even I call the getComponent recursively
Edit on codesandbox
Problem was that you didn't return anything when dealing with object
So this part
Object.keys(record).map((key, index) => {
console.log(44);
return (
<AccordionItem className="ml-5" title={`1 - ${index}`} expanded>
{getComponent(record[key])}
</AccordionItem>
);
});
should be
return Object.keys(record).map((key, index) => {
console.log(44);
return (
<AccordionItem className="ml-5" title={`1 - ${index}`} expanded>
{getComponent(record[key])}
</AccordionItem>
);
});
I added a default expanded property and now it displays all data.
Check
this sandbox
Hy, I don't know exactly what you want to display but here is a version working.
import { Accordion, AccordionItem } from "react-sanfona";
import "./styles.css";
const datalist = [
{
id: 3423235234,
name: "John",
address: [
{
first: "city1",
second: "city2"
}
]
}
];
export default function App() {
function getComponent(record) {
if (Array.isArray(record)) {
return record.map((b, index) => (
<AccordionItem className="ml-5" title={`${index}`} key={index}>
{getComponent(b)}
</AccordionItem>
));
}
if (typeof record === "object") {
return (
<div>
{Object.keys(record).map((key, index) => {
return (
<AccordionItem
className="ml-5"
title={`1 - ${index}`}
expanded
key={index}
>
{getComponent(record[key])}
</AccordionItem>
);
})}
</div>
);
}
if (typeof record === "string" || typeof record === "number") {
console.log("string or number: ", record);
return <AccordionItem className="ml-5" title={`2 - ${record}`} />;
}
return (
<AccordionItem className="ml-5" title={`3 - ${record.toString()}`} />
);
}
return (
<div className="App">
<div className="px-7">
<Accordion>{getComponent(datalist)}</Accordion>
</div>
</div>
);
}
The package you're using raise many errors in the console (with no configuration). Did you check the material ui's accordions ? https://material-ui.com/components/accordion/
This is working code, which might help you
Here, initially root folder will be hiding all children once click on root -> its direct children will be displayed/hidden and so on
//explorer is the json coming from parent component
import React, { useState } from "react";
function Accodian({ explorer }) {
const [expand, setExpand] = useState(false);
if (explorer.children) {
return (
<div>
{/* {explorer.children ? ( */}
<>
<div onClick={() => setExpand((prevState) => !prevState)}>
{explorer.name}
</div>
{expand ? (
<>
{explorer.children.map((child) => {
// return <div key={child.name} style = {{paddingLeft: '20px'}}>{child.name}</div>;
return <Accodian explorer={child} key={child.name} />;
})}
</>
) : null}
</>
{/* ) : null} */}
</div>
);
} else {
return <div style={{ paddingLeft: "20px" }}>{explorer.name}</div>;
}
}
export default Accodian;
Check this sandbox : https://codesandbox.io/s/js-recursion-accordian-v03y5q?file=/src/components/Accordian.js:0-823/

Optional array.map inside array.map

I have a menu, rendered due to an array.map BUT I want to have a submenu rendered too but I can not find the way of achieving this
for instance I have this correctly done
import React from 'react'
export const Menu = () => {
const item = [
'option1',
'option2',
'option3',
'option4',
'opction5',
]
const subMenuItem = [
'2.option1',
'2.option2',
'2.option3',
'2.option4',
'2.option5',
'2.option6',
]
return (
<>
<div>
Hola
</div>
<ul>
{
item.map((x, index) => (
<li key={index}>
{x}
</li>
))
}
</ul>
</>
)
}
but now I want to have a submenu on option2 mapping the subMenuItem array if there is a condition in which some variable is equal to an option from item
something inside the above mapping like
if (x === 'option2') {
<ul>
subMenuItem.map((y,i)=>(
<li key={i*1000}
{y}
</li>
))
</ul>
something like that
thanks in advance
Assuming you have subMenu field inside the menu item such as :
const menu = [
{"name": 'option1'},
{"name": 'option2', "subMenu": [...]},
{"name": 'option3'},
]
You can render conditionally:
return (
<>
<div>
Hola
</div>
<ul>
{
item.map((x, index) => (
<li key={index}>
<>
{x.name}
{x.subMenu && x.subMenu.map((subMenuItem, subIndex) => (
<li key ={index + subIndex}>
{subMenuItem.name}
</li>
)}
</>
</li>
))
}
</ul>
</>
)
import React from 'react'
export const Menu = () => {
const item = [
'option1',
'option2',
'option3',
'option4',
'opction5',
]
const subMenuItem = [
'2.option1',
'2.option2',
'2.option3',
'2.option4',
'2.option5',
'2.option6',
]
return (
<>
<div>
Hola
</div>
<ul>
{
item.map((x, index) => {
return ( <li key={index}>
{x}
</li>
{if (x === 'option2') {
<ul>
{
subMenuItem.map((y,i)=>(
<li key={i*1000}>
{y}
</li>
))
}
</ul>})
})
}
</ul>
</>
)
}
Try this
this is what I did
import React from 'react'
import styled from 'styled-components'
export const Menu = () => {
const LI = styled.li`
list-style: '- ';
`
const item = [
'option1',
'option2',
'option3',
'option4',
'option5',
]
const subMenuItem = [
'2.option1',
'2.option2',
'2.option3',
'2.option4',
'2.option5',
'2.option6',
]
return (
<>
<ul>
{
item.map((x, index) => (
<LI key={index}>
{x}
<ul>
{(x === 'option2') &&
subMenuItem.map((y, i) => (
<li key={i * 1000}>
{y}
</li>
))
}
</ul>
</LI>
))
}
</ul>
</>
)
}
just put the condition inside of it as it is...first time doing this , seems tricky though :P

Changing div text value by clicking on a li item

i want to change the text value in a div by clicking on a ul li item.
const text = {
value1 : 'blabla1',
value2 : 'blabla2',
value3 : 'blabla3'
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div> </div>
By clicking on li 1 the div get the value1, by clicking on li 2 the div get the value2 etc.
Can someone give me some advice, thank you
i am write code
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const text = [
{ id: 1, value: "blabla1" },
{ id: 2, value: "blabla2" },
{ id: 3, value: "blabla3" }
];
const [textSelected, setTextSelected] = useState("");
const selectLi = (id) => {
let index = text.findIndex((x) => x.id === id);
if (index === -1) return;
setTextSelected(text[index].value);
};
return (
<>
<ul>
{text.map((x) => (
<li
onClick={() => {
selectLi(x.id);
}}
>
{x.id}
</li>
))}
</ul>
<br />
<div>{textSelected}</div>
</>
);
}
Work Demo
Here is another approach:
import React, { useState } from "react";
export default function App() {
const [listValues, setListValues] = useState([1, 2, 3]);
const text = { value1: "blabla1", value2: "blabla2", value3: "blabla3" };
const showTextValue = (index) => {
const newValues = [...listValues];
newValues[index] = Object.values(text)[index];
setListValues(newValues);
};
const content = listValues.map((val, index) => (
<li key={index} onClick={() => showTextValue(index)}>
{val}
</li>
));
return (
<div className="App">
<ul>{content}</ul>
</div>
);
}
import React from "react";
import "./styles.css";
export default function App() {
const clickHandler = (e) => {
alert("value is " + e.target.id);
let item = document.getElementById(e.target.id);
console.log(item.getAttribute("name"));
};
return (
<div className="App">
<ul>
<li id="blabla1" name="el1" onClick={clickHandler}>
1
</li>
<li id="blabla2" name="el2" onClick={clickHandler}>
2
</li>
<li id="blabla3" name="el3" onClick={clickHandler}>
3
</li>
</ul>
</div>
);
}
CodeSandbox example. You can also assign a value to a variable by using useState.

Error in Component Output by Button Click

I have 2 buttons and information about div. When I click on one of the buttons, one component should appear in the div info. Where is the error in the withdrawal of the component div info?
import React, { Component } from 'react';
import Donald from '/.Donald';
import John from '/.John';
class Names extends Component {
state = {
array:[
{id:1,component:<Donald/>, name:"Me name Donald"},
{id:2,component:<John/>, name:"My name John"},
],
currComponentId: null
changeComponentName = (idComponent) => {
this.setState({currComponentId:idComponent});
};
render() {
return(
<table>
<tbody>
<tr className="content">
{
this.state.array.map(item=> item.id===this.element.id).component
}
</tr>
<button className="Button">
{
this.state.array.map( (element) => {
return (
<td key={element.id}
className={this.state.currComponentId === element.id ? 'one' : 'two'}
onClick={ () => this.changeComponentName(element.id)}
>{element.name}
</td>
)
})
}
</button>
</tbody>
</table>
)
}
}
export default Names;
You have several problems here, the first being that you are missing the closing curly bracket on your state. this.element.id is also undefined, I assume you are meaning this.state.currComponentId.
Your html is also fairly badly messed up, for example you are inserting multiple <td>s into the content of your button. I also don't see where this.changeComponentName() is defined, so I am assuming you mean this.showComponent()
The primary issue is probably in this.state.array.map(item=> item.id === this.element.id).component, as map() returns an array. An array.find() would be more appropriate, though you still need to check to see if there is a match.
I might re-write your component like this (I have swapped out the confusing html for basic divs, as I'm not sure what you are going for here)
class Names extends Component {
state = {
array: [
{ id: 1, component: <span>Donald</span>, name: "Me name Donald" },
{ id: 2, component: <span>John</span>, name: "My name John" },
],
currComponentId: null,
};
showComponent = (idComponent) => {
this.setState({ currComponentId: idComponent });
};
render() {
//Finding the selected element
const selectedElement = this.state.array.find(
(item) => item.id === this.state.currComponentId
);
return (
<div>
<div className="content">
{
//Check to see if there is a selected element before trying to get it's component
selectedElement ? selectedElement.component : "no selected."
}
</div>
{this.state.array.map((element) => {
return (
<button
className="Button"
key={element.id}
className={
this.state.currComponentId === element.id ? "one" : "two"
}
onClick={() => this.showComponent(element.id)}
>
{element.name}
</button>
);
})}
</div>
);
}
}
Errors:- (1) You are showing list inside tag, instead show as <ul><li><button/></li></ul>(2)You are not displaying content after comparison in map()This is a working solution of your question.
class Names extends React.Component {
state = {
array: [
{ id: 1, component: <Donald />, name: "Me name Donald" },
{ id: 2, component: <John />, name: "My name John" }
],
currComponentId: null
};
clickHandler = idComponent => {
this.setState({ currComponentId: idComponent });
};
render() {
return (
<div>
<ul>
{this.state.array.map(element => {
return (
<li key={element.id}>
<button
className="Button"
onClick={() => this.clickHandler(element.id)}
>
{element.name}
</button>
</li>
);
})}
</ul>
{this.state.array.map(data => {
if (this.state.currComponentId === data.id)
return <div>{data.component}</div>;
})}
</div>
);
}
}
const Donald = () => <div>This is Donald Component</div>;
const John = () => <div>This is John Component</div>;
ReactDOM.render(<Names />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root' />

Resources