How to map list of different svg components? - javascript

I have svgs that are required in variables
var SVG1 = require('babel-loader!svg-react-loader!../path/SVG1.svg?name=SVG1')
var SVG2 = require('babel-loader!svg-react-loader!../path/SVG2.svg?name=SVG2')
var SVG3 = require('babel-loader!svg-react-loader!../path/SVG3.svg?name=SVG3')
I use these variables as a component<SVG1/>, because it is a requirement of a svg-react-loader.
I map JSON
{this.props.jsonSting.map((item) =>
<Component key={item.id}
name={item.name}
/>
)}
How should I pass svg variables to Component so that with every map iteration in would return next svg.
I tried to add to JSON svg's names (SVG1,SVG2,SVG3) and then past it like this {item.svgname}, but it didn't work.
Any ideas?

Place your icons in an array then pass them based on index or some other variable:
const icons = [<SVG1/>, <SVG2/>, <SVG3/>]
this.props.jsonSting.map((item, index) =>
<Component key={item.id}
name={item.name}
icon={icons[index % 3]}
/>
)}
You can also create named collection with an object literal:
const icons = { SVG1: <SVG1/>, SVG2: <SVG2/>, SVG3: <SVG3/> }
this.props.jsonSting.map(item =>
<Component key={item.id}
name={item.name}
icon={icons[item.svgname]}
/>
)}

Related

How do I properly use the map function to get the index of the element in the array? [duplicate]

This question already has an answer here:
ESCMAScript 6 arrow functions - parentheses around parameter
(1 answer)
Closed 10 months ago.
Hello I am trying to find the index of the element in an array using map so that eventually I can create a onClick function that will change the image based on that index.
However when I add index to my map function I then get an error stating that the img is not defined.
const currentIndex = 0;
const gallery =
product.images.length > 1 ? (
<Grid gap={2} columns={5}>
{product.images.map(img, index => (
<GatsbyImage
image={img.gatsbyImageData}
key={img.id}
alt={product.title}
/>
))}
</Grid>
) : null;
The above code displays a list of thumbnail size images. I would like to eventually be able to click on each image and have the larger image appear.
Code for the larger image is below.
<div>
<GatsbyImage
image={product.images[currentIndex].gatsbyImageData}
alt={product.title}
/>
{gallery}
</div>
Simple parenthesis fix:
const currentIndex = 0;
const gallery =
product.images.length > 1 ? (
<Grid gap={2} columns={5}>
{product.images.map((img, index) => (
<GatsbyImage
image={img.gatsbyImageData}
key={img.id}
alt={product.title}
/>
))}
</Grid>
) : null;
Make sure to not pass two values, but one value to Array.map: a function with its own optional parameter called 'index'
Consider expanding your work to a function that you can just reference, to make life easier, and code more legible, like this:
const currentIndex = 0;
const mapper = (img, index) => (
<GatsbyImage image={img.gatsbyImageData} key={img.id} alt={product.title} />
);
const gallery =
product.images.length > 1 ? (
<Grid gap={2} columns={5}>
{product.images.map(mapper)}
</Grid>
) : null;
See more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#syntax

React - Display all items inside localStorage as a Material UI List

I have localStorage with multiple items on it. I want to retrieve all of them and display it on a <ListItem> Material UI.
Here's my current code:
function saveJob(key, value) {
localStorage.setItem(key, value);
}
function saveJob is basically just save the value along with unique key.
The content of the localStorage would be something like this:
Key
Value
1
Technician
2
Plumber
How I retrieved the items back:
var savedJobs = [];
useEffect(() => {
var keys = Object.keys(localStorage),
i = keys.length;
while (i--) {
savedJobs.push(localStorage.getItem(keys[i]));
}
return savedJobs;
}, [])
Now onto the problem, I tried to display it in a functional component through <ListItem> Material UI like the following:
<List>
{savedJobs.map((job) => (
<ListItem key={job.Key} disablePadding>
<ListItemButton>
<ListItemText primary={job.Value} />
</ListItemButton>
</ListItem>
))}
</List>
This is not working. Can anyone point me in the right direction?
React doesn't trigger rerender when a local variable in a function component was changed. It will be rerendered when: the state has changed, props were changed or the parent component was rerendered.
You should put your savedJobs to state and write
const [savedJobs, setSavedJobs] = useState([]);
instead of
var savedJobs = [];
You need state to store the data you retrieve from localstorage. Instead of your savedJobs var outside of your useEffect, create an array inside the hook and then update your state with this data. Here's an example:
// Initiate your state as an empty array.
const [savedJobs, setSavedJobs] = useState([]);
// Used as an example instead of localstorage.
const example = ["foo", "bar"];
useEffect(() => {
let jobs = [];
let keys = Object.keys(example),
i = keys.length;
while (i--) {
jobs.push(example[i]);
}
setSavedJobs(jobs);
}, [example]);
return <>{savedJobs.map((job) => job)}</>;
Or see this interactive example.
First I would check what is inside your savedJobs array.
It seems that savedJobs would look like this:
savedJobs = ['Technician', 'Plumber'];
On the other hand, you are mapping and getting the value like so:
{job.Key} & {job.Value}
Try:
<List>
{savedJobs.map((job, i) => (
<ListItem key={i} disablePadding>
<ListItemButton>
<ListItemText primary={job} />
</ListItemButton>
</ListItem>
))}
</List>
ps: you should not use i as a key, but I let you figuring out what value you want there.
If you want to use {job.key} and {job.value}, you need to push an object like so:
savedJobs.push({key: i, value: localStorage.getItem(keys[i])});

React render quantity of components based on a numeric value

I want to pass in a value to a component and render multiple child components based on that value. e.g. if I pass in count={4} in props, then I want to render 4 <Icon/> components. If I pass in 5, I want to render 5, and so on.
At the moment, all I can think to do is to take the value and turn it into an array (i.e. do a for loop and push a placeholder element to an array with each iteration) and then do a map on that array. But that seems like overkill.
Is there a simple solution to this?
You can do it like this:
...
return(
Array.from({length: props.count}, () => <Icon />)
)
You're probably looking a way to map amount of children to be populated. Check this live example: https://snack.expo.io/#zvona/mapping-child-components
This is the core code from that example:
const Icon = ({index}) => (
<View><Text>{`Icon #${index + 1}`}</Text></View>
);
const Icons = ({count}) => (
Array.from({length: count}).map((_item, index) => <Icon index={index}/>)
)
const App = () => {
return (
<View style={styles.container}>
<Icons count={5} />
</View>
);
}
You can use a simple for loop for this
for(let i = 0; i < count ; i++){
return <Icon />
}
If this doesn't work, you can do the following. It's a bit modern es6 function. Try this..
{[...Array(count)].map((x, i) =>
<Icon key={i} />
)}

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} />)}

React incrementing variable within .map() function

I am mapping through an array, and I want my variable i to be used as a unique key for my Components, however I do not know how (or where) to increment it correctly, if I add a {i++} within the <Component> tags then it will display the value of i on screen, and if I instead add {this.function(i)} and place the i++ inside the function, it will call the function but the variable i will reinitiate to the value of 0 everytime, so the key value will not be unique. I need the value of i to be the key for the component and it has to be incremented by 1 everytime, does anyone know how I can achieve this? Also, as you can see in the code, when the component is clicked it will make a function call which will send the value of i of the clicked component as a parameter to the called function.
Code:
function(i) {
console.log(i)
}
render() {
var i = 0;
var {array} = this.state;
return (
<div className="App">
{array.map(item => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
))}
</div>
);
}
The map function gets a second parameter which is the index of the element:
{array.map((item, i) => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
)}
Be aware that if you intend to sort this array or change its contents at runtime, then using array index as a key can cause some mistakes, as sometimes an old component will be mistake for a new one. If it's just a static array though, then using index as a key shouldn't be a problem.
.map already offer the increment, just add a second variable to the callback
render() {
var {array} = this.state;
return (
<div className="App">
{array.map((item,i) => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
))}
</div>
);
}
You could try array.map((x, Key) => console.log(key)); ..
In place of console.log you could add your code, it should work fine as per your requirement.

Categories

Resources