very simple question, when I loop through array when rendering react compnent with .map function, say:
render() {
let board = this.props.board;
return (
<div>
{
board.map((piece,index) => {
return (
<Piece data={piece}/>
);
})
}
</div>
);
}
I'm trying to add a break line every 5 pieces so (index % 5 == 0) add <br /> before the <Piece />
when I try to concatinate with + or to do something like this:
board.map((piece,index) => {
return (
(index % 5 == 0) ? <br /> : ''
<Piece data={piece}/>
);
})
I'm getting an output of matrix of [Object object]'s
Return an array of [ <br />, <Piece> ] if the condition holds and return just the Piece component otherwise. See the fiddle.
The relevant piece of code is this:
return <div>{items.map(function (i, index) {
if (index % 5 === 0) {
return [ <br key={index + "break"} />, <Item key={index} num={i} /> ];
}
return <Item key={index} num={i} />;
})}</div>;
Also, put key on the components you return from map or other ways that return array-like instances. This way, React doesn't need to take out all the generated components and replace them on each render, but can just find them under the key and update their attributes. Check out Reconciliation in React docs to learn more.
Related
I am working with bigger arrays in React, and want the following display like this: image/name image/name image/name. I have the following the code but I don't know how I can map over the images array to so it shows it's image. Thank you
function showProtocolsNames() {
if (supportedVaults) {
let arr = supportedVaults
.map((item) => item.protocolName)
.filter((item, index, arr) => {
return arr.indexOf(item) == index;
});
let arrImages = supportedVaults
.map((item) => item.protocolKey)
.filter((item, index, arr) => {
return arr.indexOf(item) == index;
});
let protocolsName = [...new Set(arr)];
let protocolsImages = [...new Set(arrImages)];
console.log(protocolsName, protocolsImages);
return protocolsName.map((vault) => {
return (
<>
{' '}
<img
src={getVaultIcon(vault)}
width="42px"
height="42px"
style={{
marginRight: '12px',
}}
/>
<p className="vaults-protocol">{vault}</p>
</>
);
});
}
return null;
}
Solved: By creating an array of the images and names together and just mapping over it like DBS suggested in comments.
I believe there is a much simpler solution to your problem than your current approach. For example, you could use the supportedVaults data immediately while mapping the image/name components, like this:
function showProtocolsNames() {
// added check to ensure there is data inside supportedVaults
if (supportedVaults.length) {
// removed the two mapped arrays
// added index which is generated by map function
return protocolsName.map((vault, index) => {
// added div instead of <> in order to include a key, which is required in a map function
return (
<div key={`${index}-${vault?.protocolKey}`}>
{" "}
<img
src={getVaultIcon(vault?.protocolKey)} // here we pass protocolKey to the getVaultIcon function
width="42px"
height="42px"
style={{
marginRight: "12px",
}}
/>
{/* here we add protocolName inside the paragraph */}
<p className="vaults-protocol">{vault?.protocolName}</p>
</div>
);
});
}
return null;
}
This logic above is based on your description of the issue, assuming protocolKey is what you need to pass to get the vault icon in getVaultIcon function and protocolName is the value you need to show as the name. If my perception is wrong, please edit your question to reflect more info on what exact data you need to get from the supportedVaults array, or what format supportedVaults has.
I want to render a JSX with some condition being true and map through an array.
below is the code,
{this.props.variables &&
this.props.variable.map((variable, index) => {
let element;
if (variable.anchor) {
element = document.querySelector(variable.anchor);
}
this.box_ref.current && element && (// error here
<Childcomponent
element1={this.box_ref.current}
anchor={variable.anchor}
/>)
}
}
)
}
There is an error saying that the expression is not an assignment or a call. how can I fix it? thanks.
You need to provide a return value for #Array.map callback.
Also, you should provide unique keys to React elements within an array:
<>
{this.props.variables &&
this.props.variable.map((variable, index) => {
let element;
if (variable.anchor) {
element = document.querySelector(variable.anchor);
}
// v Add return statement
return (
this.box_ref.current &&
element && (
<Childcomponent
key={index}
element1={this.box_ref.current}
anchor={variable.anchor}
/>
)
);
})}
</>
Hello I have a component which doesnt return anything. Im following a tutorial and the person is using newer syntax which confuses me a bit. The component looks like this:
const Alert = ({alerts}) => alerts !== null && alerts.length > 0 && alerts.map(alert => (<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg}</div>));
I simply want to know how to write this without it being single line. So i can see what's going on. Much appreciated in advance. For as far as i am aware you always need to return something.
const Alert = ({ alerts }) => {
if (alerts !== null && alerts.length > 0) {
return alerts.map(alert => (
<div key={alert.id} className={`alert-${alert.type}`}>
{alert.msg}
</div>
));
}
return null
};
Things at play here are:
Arrow Functions
Array.Map
JSX
Template Literals
Basically its a component that takes in an alerts property (Array) as a prop (<Alert alerts={[...]} />). It checks whether the passed array is present and is not empty and then maps over it. For every item in the array, we are rendering a div containing the alert message.
Hope this helps!
Very roughly (i.e., untested):
const Alert = ({alerts}) => {
if ((alerts === null) || (alerts.length === 0)) {
return null
}
return alerts.map(alert => (
<div
key={alert.id}
className={`alert-${alert.type}`}
>
{alert.msg}
</div>
))
}
const Alert = ({alerts}) => {
if (!alerts || !alerts.length) return null
return (
<>
{alerts.map(alert => (
<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg}</div>
)}
</>
)
}
I think what you are struggling with is generally the one-liner syntax, which doesn't need a return if there are no braces present.
What I mean is that this line
return alerts.map(alert => {
return (<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg} </div>)
})
Would be the same as this line
return alerts.map(alert => (<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg} </div>))
I need to sort an array of objects by a particular property and render an <Entry /> component for each one, in the correct order. The complication is that I need to place a different one-off component directly after the first <Entry />. So I need to do things in this order: 1 - Sort the array. 2 - Render the first <Entry /> followed by the one-off component. 3 - Map through the remaining sorted array and render the rest of the <Entry />s.
But React isn't letting me use .sort independently of .map.
Here's what I'd like to do:
render() {
return (
<React.Fragment>
{entries.sort((a, b) => b.likes - a.likes)}
<Entry entryNumber={entries[0].id} />
<OneOffComponent />
{entries.map((entry, index) => {
if (index > 0) {
return <Entry entryNumber={entry.id} />
}
}}
</React.Fragment>
);
}
But this code gives me an error: "Uncaught Error: Objects are not valid as a React child (found: object with keys {entryId, text, likes, user, date}). If you meant to render a collection of children, use an array instead."
A few things off with your code:
Variables assignment should be performed before returning the ReactElement.
A ReactElement should be returned in the render function, not an array.
This could be a React.Fragment, a HTML element, or a React component containing zero or more React Elements.
You can unpack the array to pick out the entry among the rest after it's sorted.
render() {
// say you have entries array from props, state or where ever ¯_(ツ)_/¯
entries.sort((a, b) => b.likes - a.likes)
const [firstEntry, ...rest] = entries;
const restEntries = rest.map(
entry => <Entry key={entry.id} entryNumber={entry.id} />
);
return (
<React.Fragment>
<Entry entryNumber={firstEntry.id} />
<OneOffComponent />
{restEntries}
</React.Fragment>
);
}
The problem comes from your map function only returning something conditionally. This will cause every entry with index <= 0 to stay as they are in your array followed by React trying to render them.
If your condition is not met, you should send out an empty node instead (I do not know if a fragment will work) :
render() {
entries.sort((a, b) => b.likes - a.likes) //Does the exact same thing
return (
<React.Fragment>
<Entry entryNumber={entries[0].id} />
<OneOffComponent />
{entries.map((entry, index) => index > 0 ?
<Entry key={entry.id} entryNumber={entry.id} />
:
<div key={entry.id}/>)
}
}}
</React.Fragment>
);
}
An even better solution would be to use shift to take the first value out of your array. Since it will also remove the first value from your array, you do not need your condition anymore :
render() {
return (
<React.Fragment>
{entries.sort((a, b) => b.likes - a.likes)}
<Entry entryNumber={entries.shift().id} />
<OneOffComponent />
{entries.map(({ id }) => <Entry key={id} entryNumber={id} />)}
</React.Fragment>
);
}
The problem is that sort() also returns the sorted array, and so it is like you are outputting that array of objects within a <React.Fragment> element:
<React.Fragment>
{entries.sort((a, b) => b.likes - a.likes)}
The sorting itself is not the problem, the place where you do it, is. So move that sorting out of the output:
entries.sort((a, b) => b.likes - a.likes);
return (
<React.Fragment>
That will fix it.
NB: Instead of the if inside the map callback, just skip the first element with slice:
entries.slice(1).map( ......
I want to create n instances of a React component.
What is a good terse way to do this give that JSX can only contain expressions?
I currently am trying the following:
<Wrapper>
{repeat(n)(i => <MyComponent i={i} />}
</Wrapper>
function repeat(n) {
return cb => Array(n).fill(null).forEach((_, i) => cb(i));
}
You can use as much JavaScript als you like :)
render() {
const lst = [1, 2, 3, 4];
return (
<div>
{lst.map(itm => <span key={itm}>{itm}</span>)}
</div>
);
}
If you do not have a key ready, you can use the second argument of the map callback which is the index in the array. More info on MDN.
In your specific case where you do not have an array but just a number:
render() {
var times = [];
for (let i = 0; i < 10; i++) {
times.push(<MyComponent key={i} i={i} />);
}
return <Wrapper>{times}</Wrapper>;
}
Also check this answer on how to use for loops. It's not quite as nice but it also works. I believe the React team has planned to make working with arrays in JSX more straight forward.
If you have just a number, and do not want to use a for loop, you could also "fake" it, for example by using string.repeat. Not sure if this is very readable though :)
render() {
return (
<div>
{'a'.repeat(10).split('').map((_, i) => <MyComponent i={i} />}
</div>
);
}