I'm creating a table by mapping my data , I'm trying to create a TableSortLable , but I only want to enable the users to sort by the first two columns .
{
Columns.map(c,index => {
return (
<TableCell key={c.key} component="th" padding="checkbox">
<TableSortLabel
active={props.brokersListOrderByColumn === c.key}
direction={props.brokersListSortOrder}
onClick={() => props.setOrderBy(c.key)}>
{c.label}
</TableSortLabel>
</TableCell>
)
})
}
I was thinking on using the onClick event, and to enable it only when its the first or second column . I tried something like:
{(index === 1 || index ===2) && onClick={() => props.setOrderBy(c.key)}}>
Or conditionally setting the onClick value , but both didn't work .
I've also tried conditionally setting the TableSortOrder :
<TableCell key={c.key} component="th" padding="checkbox">
{(index === 1 || index === 2) && <TableSortLabel
active={props.brokersListOrderByColumn === c.key}
direction={props.brokersListSortOrder}
onClick={() => props.setOrderBy(c.key)}>
{c.label}
</TableSortLabel>}
{(index !== 1 && index !== 2) && c.label}
</TableCell>
But it complains c is undefined
Syntax error. You should write parameters in brackets for functors. Like this
Columns.map((c, index) => {...
Otherwise engine will try to find (RHS) variable c (expected as function).
You didnt init such variable and get this
But it complains c is undefined .
Try onClick={(index === 1 || index === 2) ? () => props.setOrderBy(c.key) : null}
Try to give something like
onClick={
() => (index === 1 || index === 2) && props.setOrderBy(c.key)
}
Maybe you can just add check inside handler?
onClick={() => {
if((index === 1 || index ===2)) {
props.setOrderBy(c.key)}
}
}}
You can also extract array function for breivity:
const onClick = () => {
if((index === 1 || index ===2)) {
props.setOrderBy(c.key)}
}
}
....
onClick={onClick}>
Related
I'm getting an array in React/Next, but before mapping through it I'm running a few filters and sorting it. I'd like to display a simple message if nothing gets found after the filters run. It should display a span with a simple text string (jsx).
The code is pretty simple:
{ArrayObj
.filter(
(item) =>
// conditions here
)
.sort((a, b) => (a.condition > b.condition ? 1 : -1))
.map((item) => (
<li key={item._id}>
// contents of each iteration here
</li>
))}
I'm guessing it should go just right before the .map but I'm stuck at this.
The full code is as follows:
{yachtListings
.filter(
(yacht) =>
yacht.buildYear >= searchYear &&
yacht.price <= searchPrice &&
yacht.length <= searchLength &&
yacht.type === searchType
)
.filter((yacht) =>
filterKeyword == ''
? yacht
: yacht.modelName
.toLowerCase()
.includes(filterKeyword.toLowerCase())
)
.sort((a, b) => (a[sortOrder] > b[sortOrder] ? 1 : -1))
.map((yacht) => (
<li key={yacht._id}>
<SecondHandCard /> // card components with props
</li>
))}
You can see a live demo here. I'm a front-end guy taking my skills to the back-end more and more.
You could filter the array first and then conditionally show jsx depending on the length of that.
let filteredArray = ArrayObj.filter((item) => {...}).sort(...);
{filteredArray.length == 0 ? <span>Message</span> : filteredArray.map(...)}
Having some issues on this, my code is supposed to hit an API, return a list of counties in a state, and display them. It does display just fine, however I want them to be in alphabetical order. Because this is a react component, I cannot understand where I would do a sort function, here is the part of the function that is mapped :
{error ? (
<h1>{error.message}</h1>
) : (
counties.map(function (county, index) {
if (
county.location.split(", ")[1] === stateName &&
county.location.split(" ")[0] !== "Unassigned" &&
county.location.split(" ")[0] !== "Out"
) {
return (
<div className="card" key={index}>
<h3>{county.location.split(",")[0]}</h3>
<p>confirmed: {county.confirmed}</p>
<p>dead: {county.dead}</p>
</div>
);
} else {
return null;
}
})
)}
</div>```
Sort right before mapping. Using this you might have duplicate code. What you can do instead is 1) map the fix the way you are handling the response, 2) sort using your own sorting function, since county seems to be JSON, and 3) Finally, map your stuff and return the JSX (the only thing that is rendered is the final return)
{error ? (
<h1>{error.message}</h1>
) : (
counties.sort(/* Sort function goes here */).map(function (county, index) {
if (
county.location.split(", ")[1] === stateName &&
county.location.split(" ")[0] !== "Unassigned" &&
county.location.split(" ")[0] !== "Out"
) {
return (
<div className="card" key={index}>
<h3>{county.location.split(",")[0]}</h3>
<p>confirmed: {county.confirmed}</p>
<p>dead: {county.dead}</p>
</div>
);
} else {
return null;
}
})
)}
</div>
Maybe this will help you.
Universal sorting function:
const sortDataByProperty = (data, property) => {
const sorted = [...data].sort((a, b) => {
return a[property].toLowerCase().localeCompare(b[property].toLowerCase());
});
return sorted;
};
Your Reactjs code:
{error ? (
<h1>{error.message}</h1>
) : (
// first parameter is data object
// second is object property by which you want to sort the data
const sortedData = sortDataByProperty(counties, 'name');
sortedData.map(function (county, index) {
if (
county.location.split(", ")[1] === stateName &&
county.location.split(" ")[0] !== "Unassigned" &&
county.location.split(" ")[0] !== "Out"
) {
return (
<div className="card" key={index}>
<h3>{county.location.split(",")[0]}</h3>
<p>confirmed: {county.confirmed}</p>
<p>dead: {county.dead}</p>
</div>
);
} else {
return null;
}
})
)}
</div>
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'm working inside a functional React component and I'm trying to render a 'Card' component with a conditional onClick based on the member's typename. A card should be clickable if its typename is 'Bundle' or 'LegacyArticle'. Any other typename should not have an onClick property.
I'm having trouble finding an efficient way to apply an onClick to a card without having a conditional with a bunch of lines of repeated code, (the code that would essentially be nested children of the 'Card' component).
So far, I've been able to render it conditionally from a function. This allows my return statement to have better readability but there is still a big chunk of code that gets repeated, and I want to find a way to reduce that.
return (
<div css={cards}>
{members && members.map((member, index) => (
renderCard(member, index)
))}
</div>
);
const renderCard = (member, index) => {
const isClickable = member.__typename === 'Bundle' || member.__typename === 'LegacyArticle';
if (isClickable) {
return <Card key={index} css={card} onClick={() => onCardClick(member)}>
{(member.__typename !== 'LessonSpark' &&
schemas[member.__typename].image(member)) &&
(<CardImage src={schemas[member.__typename].image(member)} />)}
<CardBlock css={cardType}>
{member.label || schemas[member.__typename].typename}
</CardBlock>
<CardBlock css={cardTitleStyle}>
{_truncate(schemas[member.__typename].title(member), 60)}
</CardBlock>
</Card>
} else {
return <Card key={index} css={card}>
{(member.__typename !== 'LessonSpark' &&
schemas[member.__typename].image(member)) &&
(<CardImage src={schemas[member.__typename].image(member)} />)}
<CardBlock css={cardType}>
{member.label || schemas[member.__typename].typename}
</CardBlock>
<CardBlock css={cardTitleStyle}>
{_truncate(schemas[member.__typename].title(member), 60)}
</CardBlock>
</Card>
}
};
As you can see, <Card> has children nested, and it's a lot of lines of code being repeated when essentially, the only difference is one has an onClick and one doesn't.
Any ideas on how I could possibly reduce this code and find a clean way to apply an onClick conditionally?
You can basically use DRY principle by only changing the unique / custom parts. So in this case the only difference i could see between the if and else statements was the onClick, so you can instead put the conditional there, if you want an onClick, in this case use a ternary or you could use &&.
const renderCard = (member, index) => {
const typename = member.__typename;
const isClickable = typename === 'Bundle' || typename === 'LegacyArticle';
const schema = schemas[typename];
const image = schema.image(member);
const cardContents = (typename !== 'LessonSpark' &&
image) &&
(<CardImage src={image} />);
return (<Card key={index} css={card} onClick={ isClickable ? () => onCardClick(member) : undefined}>
{cardContents}
<CardBlock css={cardType}>
{member.label || schema.typename}
</CardBlock>
<CardBlock css={cardTitleStyle}>
{_truncate(schema.title(member), 60)}
</CardBlock>
</Card>);
}
In addition to that, you can save the values from function calls and from properties of objects so that a) the code is easier to read/follow, and b) the code is a bit more optimized, since it is not doing unnecessary extra function calls.
One way to accomplish this is by adding the condition inside your Card onClick prop:
const isClickable = member.__typename === 'Bundle' || member.__typename === 'LegacyArticle';
return <Card key={index} css={card} onClick={isClickable ? () => onCardClick(member): null}>
{(member.__typename !== 'LessonSpark' &&
schemas[member.__typename].image(member)) &&
(<CardImage src={schemas[member.__typename].image(member)} />)}
<CardBlock css={cardType}>
{member.label || schemas[member.__typename].typename}
</CardBlock>
<CardBlock css={cardTitleStyle}>
{_truncate(schemas[member.__typename].title(member), 60)}
</CardBlock>
</Card>
Put onClick into an object and use prop spread.
const renderCard = (member, index) => {
const typename = member.__typename;
const maybeOnClick = typename === 'Bundle' || typename === 'LegacyArticle' ?
{ onClick: () => onCardClick(member) } : {};
const schema = schemas[typename];
const image = schema.image(member);
const cardContents = (typename !== 'LessonSpark' &&
image) &&
(<CardImage src={image} />);
return (<Card key={index} css={card} {...maybeOnClick}>
{cardContents}
<CardBlock css={cardType}>
{member.label || schema.typename}
</CardBlock>
<CardBlock css={cardTitleStyle}>
{_truncate(schema.title(member), 60)}
</CardBlock>
</Card>);
}
I am working on the render method of a datatable component where I have to filter some headers.
I have this:
<TableRow>
{headers.map(header =>
checkboxes.map(checkbox =>
// HERE I NEED TO FILTER IT
checkbox.value === header.key && checkbox.checked && (
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
),
),
)}
</TableRow>
The checkbox.checked you see above returns true or false and I need 2 keys to ignore from those conditionals. It doesn't matter if the conditionals are met.
Here I am checking that the values of checkbox and header are the same checkbox.value === header.key but I need to exclude 2 header.key which are header.key === device and header.key === ticketNumber.
So I need to do something like:
<TableRow>
{headers.map(header =>
checkboxes.map(checkbox =>
// HERE I NEED TO FILTER IT
checkbox.value === header.key && checkbox.checked && (ignore header.key === 'device' && header.key === 'ticketNumber' from the conditionals above) (
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
),
),
)}
</TableRow>
I mean, those 2 headers I mentioned in the code above should never be hidden from the UI. When this condition checkbox.value === header.key && checkbox.checked is met, it will hide all of the headers with checkbox.checked === true, but I need a proper condition/function to ignore the header keys header.key === device and header.key === ticketNumber. Those 2 headers should always exist in the UI.
I am using lodash, I don't know if I can use it there or if there are any other options?
If I correctly understand your requirements, I think this should work:
<TableRow>
{headers.map(header =>
(['device', 'ticketNumber'].includes(header.key) || checkboxes.find(checkbox => header.key === checkbox.value).checked) &&
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
)}
</TableRow>
You can create a simple shouldShow() method outside of the component. The method should include your conditions, and return a boolean.
Since you've got 3 checkboxes, and you only want to show the relevant column, when one of them is checked, you should remove the 2nd map, and use Array.some() to check if at least one of the is checked, and it's value is the same value of the key.
Note: You don't need to actually filter the array, because react ignores false values in render (see conditional rendering). It also ignores undefined, and null.
const shouldShow = ({ key }, checkboxes) =>
key === 'device' ||
key === 'ticketNumber' ||
checkboxes.some(({ checked, value }) => checked && value === key);
<TableRow>
{headers.map(header =>
shouldShow(header, checkboxes) && (
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
),
)}
</TableRow>
A better solution would to create a Set of checked keys using Array.reduce(), and then check if the key exists in it while rendering the columns:
// in the render method
const shouldRenderSet = checkboxes.reduce((r, { checked, value }) =>
checked ? r.add(value) : r
, new Set(['device', 'ticketNumber']))
<TableRow>
{headers.map(header =>
shouldRenderSet.has(header.key) && (
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
),
)}
</TableRow>