I am trying to map through an object that is passed as prop and want to display different HTML elements for different object values
Object;
const allDesc = {description: "Title Description", description1: "Intro
Description", description3:"Sub title", description3: "Sub Description"}
Code:
<div>
{Object.keys(allDesc).map((desc, index) => {
if (allDes[desc] !== "") {
return (
<>
<h1>allDesc.description</h1>
<p>allDesc.description1</p>
<h3>allDesc.description2</h3>
<p>allDesc.description3</p>
</>
);
}
})}
</div>
This approach displays nothing, what would be the correct approach for mapping through an object and displaying different HTML elements for different object values. Thanks!
map over the Object.entries. If the key matches "description" return the value as an <h1>, otherwise return the value as a <p>.
function Example({ allDesc }) {
return (
<div>
{Object.entries(allDesc).map(([key, value]) => {
if (key === 'description') return <h1>{value}</h1>;
if (key === 'description3') return <h3>{value}</h3>;
return <p>{value}</p>;
})}
</div>
);
};
const allDesc = {description: 'Title Description', description1: 'Intro Description', description3: 'Another Description', description4: 'More text' };
ReactDOM.render(
<Example allDesc={allDesc} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Please try this.
<div>
{Object.keys(allDesc).map((desc, index) => {
if (allDes[desc] !== "") {
return (
<>
{
desc === 'description' ? <h1>allDes[desc]</h1> :
<p>allDes[desc]</p>
}
</>
);
}
})}
</div>
this allDes[desc] is have the value of all field you loop from the Object keys.
Related
Lets say I have an array of objects:
const options = [
{
text: "this is the text",
description: "The word 'test' should be bold"
},
{
text: "this is the text",
description: "The word 'another-word' should be bold"
}
]
the component renders something like this:
return (
{
options.map(option => {
<p className="text-green-500">{option.description}</p>
})
}
)
Now I have to make the word/s "test" and "another-word" bold respectively. In some cases it has to only be characters inside a word.
I answered it by replacing the value of description.
instead of:
description: "this is the text"
I changed it too:
description: <div>this is the <strong>text></strong></div>
You could create a function which replaces the string in '' with a <b> wrappers.
const highlight = (text) => text.replace(/'([^']+)'/g, "<b>$1</b>")
Then set the innerHTML like this:
return options.map(({ description }) =>
<p dangerouslySetInnerHTML={{ __html: highlight(description) }}></p>
)
You need to sanitize the string before doing this if these string are user inputs.
const highlight = (text) => text.replace(/'([^']+)'/g, "<b>$1</b>")
function App() {
const options = [{
text: "this is the text",
description: "The word 'test' should be bold"
},
{
text: "this is the text",
description: "The word 'another-word' should be bold"
}
]
return options.map(({ description }) =>
<p dangerouslySetInnerHTML={{ __html: highlight(description) }}></p>
)
};
ReactDOM.render(<App />, 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"></div>
You can avoid the innerHTML route entirely. In that case, you need split each string into fragments and then render them.
const highlight = (text) =>
Array.from(text.matchAll(/([^']*)'*([^']*)'*([^']*)/g), ([m, p1, p2, p3]) =>
m ? (
<>
{p1}
<b>{p2}</b>
{p3}
</>
) : null
);
And render it like this:
return options.map(({ description }) =>
<p>{highlight(description)}</p>
)
This is probably the easiest way to do it, just return a chunk of JSX from a function that deals with bolding the part of the text.
Stackblitz to run the code: https://stackblitz.com/edit/react-ts-gewz6v?file=App.tsx,index.tsx
const options = [
{
text: 'this is the text',
description: "The word 'test' should be bold",
},
{
text: 'this is the text',
description: "The word 'another-word' should be bold",
},
];
const boldText = (text) => {
const termToBold = text.match(/'([^']+)'/)[1];
const startIndex = text.toLowerCase().indexOf("'");
return (
<React.Fragment>
{text.slice(0, startIndex)}
<strong>
{text.slice(startIndex, startIndex + 2 + termToBold.length)}
</strong>
{text.slice(startIndex + 2 + termToBold.length)}
</React.Fragment>
);
};
return (
<div>
{' '}
{options.map((option) => {
return <p className="text-green-500">{boldText(option.description)}</p>;
})}
</div>
);
I have an array with objects I am using map to render the elements on the page, The problem is that I want to get the last object from the array (in my case it is 'documents') and set a class for it to style it, you can also see my code in codesandbox
export default function Nav() {
const [navItems] = useState([
{
id: 1,
name: "Home",
link: "/dashboard"
},
{
id: 2,
name: "Investments",
link: "/investments"
},
{
id: 3,
name: "Organize",
link: "/organize"
},
{
id: 4,
name: "Documents",
link: "/documents"
}
]);
return (
<div className="App">
{navItems.map((i) => {
return (
<div key={i.id}>
<p>{i.name}</p>
</div>
);
})}
</div>
);
}
Check the index of the item while mapping through it. And check if the index is equal to the navItems.length - 1. Check this-
<div className="App">
{navItems.map((item, index) => {
return (
<div
key={item.id}
className={index === navItems.length - 1 ? 'your-desired-class': ''}>
<p>{item.name}</p>
</div>
);
})}
</div>
I'm unable to show conditional output with the ternary operator. I want to pass a value to a function and show only related info from the state. My code:
import React, {useState} from 'react';
function Tasks({taskId, index}){
{task.parentId == taskId : } //Unable to code this.
return( //show only tasks where parentId == taskId
<div>
<div> {task.title} </div>
<div> {task.body} </div>
</div>
)
}
function App(){
const[tasks, setTasks] = useState([
{
taskId: 1,
title: 'Task1',
body: 'This is the body of the task1',
isComplete: false,
parentId: 0
},
{
taskId: 2,
title: 'Task2',
body: 'This is the body of the task2',
isComplete: false,
parentId: 1
},
{
taskId: 3,
title: 'Task3',
body: 'This is the body of the task3',
isComplete: false,
parentId: 1
},
{
taskId: 4,
title: 'Task4',
body: 'This is the body of the task4',
isComplete: false,
parentId: 3
}
])
return(
<div style={{marginLeft: 20}}>
<h1>ToDo</h1>
{tasks.map((task, index)=>
<Tasks
taskId=1
/>
)}
</div>
)
}
export default App;
So, I want to only show the tasks that have the parentId as 1. How should I go about this?
If you're trying to render only those tasks with the specified id, you may not have to use the ternary operator.
function renderTasks(id) {
return tasks
.filter(({ taskId }) => taskId == id)
.map(({ title, body }) => (
<div>
<div> {title} </div>
<div> {body} </div>
</div>
));
}
For the least modification to the code, you can return an empty fragment or null:
function Tasks({ task }) {
return task.parentId == task.taskId
? (
<div>
<div> {task.title} </div>
<div> {task.body} </div>
</div>
)
: null;
}
(make sure to use parentId, not pasrentId, and task.taskId, not taskId - you aren't passing task as a prop currently, so change the code to do so: <Tasks task={task} />)
But I think it'd make more sense to use .filter in the caller:
return (
<div style={{ marginLeft: 20 }}>
<h1>ToDo</h1>
{tasks
.filter(task => task.parentId === task.taskId)
.map(task => <Task task={task} />)
}
</div>
)
(since Tasks renders a single task, consider calling it Task instead of Tasks)
I have a React component that's receiving a data object returned from an API response.
I'm trying to write a function that accepts a field from that data response, checks an element inside of that field and iterates over it checking each object inside the array for the value of a specific alert.
If a value for a specific alert is found I need to render an Icon for that alert.
The data object looks like this:
location: {
...,
details: {
summary: [
{
type: 'calling',
icon: 'phone'
},
{
type: 'power',
icon: 'electric'
},
{
type: 'water',
icon: 'water-icon'
},
]
}
}
And here's the section where I'm trying to conditionally render the icons (this was my first pass and rudimentary attempt):
<div>
{location.alertDetails && (
<IconBox title={`Alerts`}>
<IconSection>
{location.details.summary.includes(type === calling) &&
<CallIcon />
}
{location.details.summary.includes(type === power) &&
<ElectricIcon />
}
{location.details.summary.includes(type === water) &&
<WaterIcon />
}
</IconSection>
</IconBox>
)}
</div>
You may store within components state the array of fetched types:
const [types, setTypes] = useState(location.details.summary.map(({type}) => type))
With that, you may simply render (or not) your icons conditionally:
<div>
{location.alertDetails && (
<IconBox title={`Alerts`}>
<IconSection>
{types.includes('calling') && <CallIcon />}
{types.includes('power') && <ElectricIcon />}
{types.includes('water') && <WaterIcon />}
</IconSection>
</IconBox>
)}
</div>
Here's the demo (with all of your components rendered as a <div>'s, since I dont have those):
const { render } = ReactDOM,
{ useState } = React
const apiData = {location:{details:{summary:[{type:'calling',icon:'phone'},{type:'power',icon:'electric'},{type:'water',icon:'water-icon'},]}}}
const IconTray = ({data}) => {
const [types, setTypes] = useState(data.location.details.summary.map(({type}) => type))
return (
<div>
{data.location.details && (
<div>
<div>
{types.includes('calling') && <div>I am a CallIcon</div>}
{types.includes('power') && <div>I am an ElectronIcon</div>}
{types.includes('water') && <div>I am a WaterIcon</div>}
</div>
</div>
)}
</div>
)
}
render (
<IconTray data={apiData} />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
You can use a map to easily iterate over these -
<div>
{location.alertDetails && location.details.summary.map(item =>{ item.icon && return (
<IconBox title={`Alerts`}>
<IconSection>
{location.details.summary.includes(type === calling) &&
<CallIcon />
}
{location.details.summary.includes(type === power) &&
<ElectricIcon />
}
{location.details.summary.includes(type === water) &&
<WaterIcon />
}
</IconSection>
</IconBox>
)})}
</div>
I'm generating multiple inputs and want to bind their values to array stored in state. I'm using an array because the number of inputs varies and I can't create them statically.
I'm assigning their value and it works as expected:
this.setState({
wordInputs: this.state.userTitle.map((word) => {
let input = <input type="text" key={i} value={this.state.userTitle[i]} onChange={this.checkWordInput}/>
i++
return input
})
})
Now I want to handle user input with checkWordInput() and here comes my question: how do I access input's key property set earlier in order to update the this.state.userTitle array? Or is there a different way to do this?
I think you don't need store inputs in state, you can move inputs to render, like so
class App extends React.Component {
constructor() {
super();
this.state = {
userTitle: [
'title - 1',
'title - 2',
'title - 3',
'title - 4'
]
};
}
checkWordInput(index, e) {
this.setState({
userTitle: this.state.userTitle.map((title, i) => (
i === index ? e.target.value : title
))
})
}
render() {
const inputs = this.state.userTitle.map((title, index) => (
<input
type="text"
key={index}
value={title}
onChange={ (e) => this.checkWordInput(index, e) }
/>
));
return (
<form>
{ inputs }
{ this.state.userTitle.map((title, i) => <p key={i}> { title }</p>) }
</form>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>