loop over objects in array in a function then output values - javascript

I'm attempting to loop over an array of objects from a config file in a react app, and print out on the page the values of "headline, problem, and fix". I am attempting to loop over each object in the array in loopMessages function. But nothing is working to get the values to show up on the page. Is there a way to get my values to appear?
CONFIG:
`const messages = [
{
headline: "some headline",
problem: "some text here for description.",
fix: "some solution"
},
{
headline: "some headline",
problem: "some text here for description.",
fix: "some solution"
},
{
headline: "some headline",
problem: "some text here for description.",
fix: "some solution"
},
{
headline: "some headline",
problem: "some text here for description.",
fix: "some solution"
},
{
headline: "some headline",
problem: "some text here for description.",
fix: "some solution"
},
{
headline: "some headline",
problem: "some text here for description.",
fix: "some solution"
}
]
export default messages;`
import styles from "./styles.css";
import messages from "../../config/messages.js";
const loopMessages = () => {
Object.values(messages).forEach((value) => {
return <p>value.headline<p>
<p>value.problem<p>
<p>value.fix<p>
});
});
};
const Guidlines = () => {
return (
<>
<div className="sub-heading-container">
<h3 className="sub-heading">Messages</h3>
</div>
<div className="guide-container">
<div className="square">
{loopMessages()}
</div>
</div>
</>
);
};
export default Guidlines;
I tried using Object.values with a forEach for my loop but my page is still blank and not printing out each object.
const loopMessages = () => {
Object.values(messages).forEach((value) => {
return <p>value.headline<p>
<p>value.problem<p>
<p>value.fix<p>
});
});

loopMessages() is not returning any values, try with map and return
const loopMessages = () => {
return Object.values(messages).map((value) => {
return (
<>
<p>{value.headline}</p>
<p>{value.problem}</p>
<p>{value.fix}</p>
</>
);
});
};

You could probably just do:
messages?.forEach(message => {
return
<>
<p>{message.headline}<p>
<p>{message.problem}<p>
<p>{message.fix}<p>
</>
})
Here messages is an array and forEach loops through each message element(object) and message.keyname should give you the value
You should wrap all the < p> tags under one tag before returning it

Your loopMessage() is not returning anything that's why it is not working.
try to make your loopMessage() like this. with map method. code below
const loopMessage=()=>{
const result = messages.map((item)=>{
return(<div>
<p>{item.headline}</p>
<p>{item.problem}</p>
<p>{item.fix}</p>
</div>
)
})
return result;

Related

Make certain characters or letters in object value bold react

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>
);

react Map is not returning any jsx

I am looping over the people array and getting the first array. My screen should say "person 1". but it is blank and
nothing is being returned.
const people = [
[
{
name: 'person 1'
}
],
[
{
name: 'person 2'
}
],
]
export default function Index() {
return (
<>
{people.slice(0,1).map((person) => {
return <h1>{person.name}</h1>
})}
</>
)
}
the code works when i do this, but I need to use slice
export default function Index() {
return (
<>
{people[0].map((person) => {
return <h1>{person.name}</h1>
})}
</>
)
}
people.slice(0, 1), unlike you'd expect, returns [[{ name: "person 1" }]], not [{ name: "person 1" }] (Array#slice returns a "slice" of an array, and in your special case it's an array with a single value, but not the single value itself). You'll have to access the inner array to get what you want:
// in your JSX
people.slice(0, 1).map(([person]) => (
<h1>{person.name}</h1>
))
Notice that the argument destructures the input array (this assumes each value in people is an array with exactly one element; if not, loop over that content).
Another option would be to Array#flatMap to "un-nest" those values:
// in your JSX
people.slice(0, 1).flatMap(person => (
<h1>{person.name}</h1>
))
This will work:
return (
<>
{people.slice(0,1).map((person) => {
return <h1>{person[0].name}</h1>
})}
</>
)
Because each person is still an array, you can't access the object's properties directly without first accessing the array that wraps it.
You have to modify the data structor of people.
const people = [
{
name: 'person 1'
},
{
name: 'person 2'
}
];
In you case, person in map method is Array type. So, person.name's value will be undefined.

Pushing array with useState()

Everyone! i just have this kind of problem that i can`t fix.
this is my App.js
import { useState } from "react"
import Header from "./components/header"
import FeedbackList from "./components/FeedbackList"
import FeedbackData from "./data/FeedbackData"
function App() {
const [feedback, setFeedback] = useState(FeedbackData)
return (
<>
<Header />
<div className="container">
<FeedbackList feedback={feedback} />
</div>
</>
) }
export default App
and this is my second js file that i want to use "feedback" prop like array
import FeedbackItem from "./FeedbackItem"
import FeedbackData from "../data/FeedbackData"
function FeedbackList(feedback) {
return (
<div>
{feedback.map((item)=>(
<FeedbackItem key={item.id} item={item}/>
))}
</div>
)
}
i cant use feedback.map function in this case because feedback is not like array (sorry for my bad english) i solve this problem without using hooks but i want to know what i can do in this case,sorry if im writing something wrong im just new in React and trying to learn.
In Javascript there are object and array. Both can be mapped.
For Array.
const arr = [
{ name: "name 1", id: "01" },
{ name: "name 2", id: "02" },
{ name: "name 3", id: "03" },
];
arr.map(item=> (<div key={item.id}>{item.name}</div>))
For Object.
const obj = {
"item01": { name: "name 1" },
"item02": { name: "name 1" },
"item03": { name: "name 1" },
};
Object.keys(obj).map((key)=> <div key={key}>{obj[key].name}</div>)
Check your type, console.log(typeof FeedbackData).
If it is not type error. Instead of
const [feedback, setFeedback] = useState(FeedbackData)
Try this.
const [feedback,setFeedback] = useState([])
useEffect(()=> setFeedback(FeedbackData),[])
what you want is to destructure the prop you need from the 'props' object.
function Component1() {
const [val, setVal] = useState([]);
return <Component2 val={val} />
}
function Component2({ val }) {
return val.map...
}
this is equivalent to doing:
function Component2(props) {
return props.val.map...
}
this is because props is an object, and so you need to get the right key from the object based on the prop name, either by destructuring or accessing it via props.propName

Pass Component from JS Object as props in React

I'm reading from a JS object (from JSX) and trying to pass value of a component but it's rendered as string.
I tried placing components (in icon key of data see below) in {} but that doesn't help as data gives an error.
Here's the simplified version of the files.
data.js as below:
const data = [
{
title: "some title",
desc: "some desc",
},
[
{
icon: "<TwitterIcon />",
title: "title 1",
desc: "desc 1",
},
{
icon: "<FacebookIcon />",
title: "title 2",
desc: "desc 2",
},
],
]
export { data }
index.js that reads data object and passes as props to AnotherComponent:
import { data } from "../path/to/data"
import AnotherComponent from "../path/to/AnotherComponent"
const Homepage = () => {
return (
<AnotherComponent {...data} />
)
}
AnotherComponent.jsx as below:
import {TwitterIcon, FacebookIcon} from "../path/to/CustomIcons"
const AnotherComponent = ({ ...data}) => {
return (
{data[1].map(item => (
<div>{item.icon}</div> // this prints string as opposed to rendering the component
<div>{item.title}</div>
<div>{item.desc}</div>
))}
)
}
index.js returns:
<div><TwitterIcon /></div>
<div>title 1</div>
<div>desc 1</div>
<div><FacebookIcon /></div>
<div>title 2</div>
<div>desc 2</div>
In the object you are defining as:
{
icon: "<TwitterIcon />",
title: "title 1",
desc: "desc 1",
}
Don't use "<TwitterIcon />" It will always return a string, instead use TwitterIcon:
{
icon: TwitterIcon,
title: "title 1",
desc: "desc 1",
}
And finally, call it where you need it, in this way:
const AnotherComponent = ({ ...data}) => {
return (
{data[1].map(item => (
<div><item.icon /></div> // HERE: check I'm calling item.icon as React Component
<div>{item.title}</div>
<div>{item.desc}</div>
))}
)
}
In this way you are passing the icon to anywhere you want and not just passing a string. So, you can call it as a Component when you need it to render. I do it a lot in my work.
I think you should pass directly the icon component in the object, like this:
const data = [
{
title: "some title",
desc: "some desc",
},
[
{
icon: <TwitterIcon />,
title: "title 1",
desc: "desc 1",
} ...
Then in index.js you can do (it is more clear to pass down props like this):
const Homepage = () => {
return (
<AnotherComponent data={data} />
)
}
In AnotherComponent.jsx now you can do:
const AnotherComponent = ({data}) => {
return (
{data[1].map(item => (
<div>{item.icon}</div>
<div>{item.title}</div>
<div>{item.desc}</div>
))}
)
}

Filtering function running one step too late

I'm building a component to filter an array of data.
There are 2 filters, one for the Team and one for the location. When each of the dropdowns are changed I'm changing a state variable for team and location and then running the filter function.
For some reason, the filter is running one step too late.
As an example - if I choose Team A, nothing will update. If I then choose Team B the careersDataFiltered variable will show Team A. If I choose Team C it'll then show Team B's data.
Its almost like its running one step too late.
You can see from the code that I'm running the filter after the state variables have been set which is why this is a bit of head-scratcher for me.
import React, { useState } from "react"
import { motion, AnimatePresence } from "framer-motion"
const careersData = [
{
name: "X Job Title",
url: "/url/x",
team: "Team a",
location: "London",
},
{
name: "M Job Title",
url: "/url/m",
team: "Team a",
location: "London",
},
{
name: "B Job Title",
url: "/url/b",
team: "Team c",
location: "Sheffield",
},
{
name: "A Job Title",
url: "/url/a",
team: "Team b",
location: "London",
},
{
name: "F Job Title",
url: "/url/f",
team: "Team b",
location: "Sheffield",
},
{
name: "C Job Title",
url: "/url/c",
team: "Team c",
location: "London",
},
{
name: "Q Job Title",
url: "/url/q",
team: "Team a",
location: "Sheffield",
},
]
const uniqueTeams = []
const uniqueLocations = []
// Build the unique values
if (careersData !== null) {
careersData.map(career => {
if (uniqueTeams.indexOf(career.team) === -1) {
return uniqueTeams.push(career.team)
}
})
}
if (careersData !== null) {
careersData.map(career => {
if (uniqueLocations.indexOf(career.location) === -1) {
return uniqueLocations.push(career.location)
}
})
}
// reorder ready for output
uniqueTeams.sort()
uniqueLocations.sort()
const CurrentVacancies = () => {
const [careersDataFiltered, setCareersDataFiltered] = useState(careersData)
const [filterTeam, setFilterTeam] = useState("")
const [filterLocation, setFilterLocation] = useState("")
// filter array based on values
const runFilter = () => {
// reset the filter data
setCareersDataFiltered(careersData)
if (filterTeam !== "") {
setCareersDataFiltered(
careersDataFiltered.filter(career => career.team === filterTeam)
)
}
if (filterLocation !== "") {
careersDataFiltered(
careersDataFiltered.filter(career => career.location === filterLocation)
)
}
console.log(careersDataFiltered)
}
return (
<>
<div className="flex">
<h2 className="mr-auto">Current Vacancies</h2>
<div className="">
<select
onChange={e => {
setFilterTeam(e.target.value)
runFilter()
}}
>
<option value="">Team</option>
{uniqueTeams.map(team => (
<option key={team} value={team}>
{team}
</option>
))}
</select>
<select
onChange={e => {
setFilterLocation(e.target.value)
runFilter()
}}
>
<option value="">Location</option>
{uniqueLocations.map(location => (
<option key={location} value={location}>
{location}
</option>
))}
</select>
</div>
</div>
<div>
<AnimatePresence>
{careersDataFiltered.map((career, index) => (
<motion.div
key={index}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
positionTransition
>
<div>
<div className="text-sm">
{career.name} - {career.team} | {career.location}
</div>
</div>
</motion.div>
))}
</AnimatePresence>
</div>
</>
)
}
export default CurrentVacancies
First of all Your code has some errors, you should use setCareersDataFiltered() in second if statement of runFilter(). Also setCareersDataFiltered() is async so you can not use careersDataFiltered right after you call setCareersDataFiltered(). It's same for call to setFilterTeam() because you are using filterTeam in runFilter() so you can not call runFilter() after your call to setFilterTeam() because your data is not guaranteed to be updated. It's also true for setFilterLocation().
Put runFilter() inside useEffect() so it will call it when any dependencies of runFilter() changes.
useEffect(() => {
runFilter();
}, [runFilter]);
Also change runFilter() as follow and use useCallback() to prevent re-render loop.
// filter array based on values
const runFilter = useCallback(() => {
let filter = careersData;
if (filterTeam !== "") {
filter = filter.filter(career => career.team === filterTeam);
}
if (filterLocation !== "") {
filter = filter.filter(career => career.location === filterLocation);
}
setCareersDataFiltered(filter);
}, [filterLocation, filterTeam]);
At the end remove any manual call to runFilter();
As a hint I think your filter logic has a bug also because I can set team to A and then it only show me A but if I set location it apply location filter and ignore A filter and it's because of async issue. So if you want to apply both filters at the same time you should mix two if statements in runFilter(). I've mixed filters and wrote a new filter routine.
Here is my working demo version
Ciao, this problem could be related on how you set careersDataFiltered in function runFilter. For my personal experience, read and set state directly is never a good idea. Try to change runFilter like this:
const runFilter = () => {
let temp_careersDataFiltered = _.cloneDeep(careersData); //here you have to clone deep careersData because javascript keep reference (I use cloneDeep from lodash)
if (filterTeam !== "") {
temp_careersDataFiltered = careersData.filter(career => career.team === filterTeam)
}
if (filterLocation !== "") {
temp_careersDataFiltered = careersData.filter(career => career.location === filterLocation)
}
setCareersDataFiltered(
temp_careersDataFiltered
)
console.log(temp_careersDataFiltered)
}
Explanation: since you don't have certainty that after call setCareersDataFiltered value of careersDataFiltered is immediately available, apply filters to temp_careersDataFiltered temporary array and in the end set careersDataFiltered to re-render the component.
I'm cloning careersData because you know javascript works with reference and if you write let temp_careersDataFiltered = careersData if you modify temp_careersDataFiltered infact you are modifying careersData also.

Categories

Resources