In my React Native app, I want to render a <View> conditional on variable var. There are two ways I've tried doing this:
1)
{var && <View/>}
{var ? <View/> : null}
Is there an advantage of one over the other?
The difference is in method 2, the falsey expression can be rendered. Take this example, which will render <div>0</div> instead of an empty div as you might expect.
render() {
const count = 0;
return (
<div>
{count && <View />}
</div>
);
}
Conditional rendering docs.
I am using React and I have an array of data objects ("items"). I am trying to display this list of objects such that each object is its own component. So I'm calling "items.map" to map each item to a component and render it, like this:
return (
<Fragment>
<h1>Items</h1>
<Card.Group itemsPerRow={8} >
{items.length > 0 ? ( items.map( (_item) => (
<Fragment key={_item.key}>
<MyItem example={4} item={_item} />
</Fragment>
))
) : (<p>No items!</p>)}
</Card.Group>
</Fragment>
)
Here is the code for MyItem:
const MyItem = (example, item) => {
console.log(example);
console.log(item);
return (<Fragment>
<div style={{margin: "1em"}}>
<Card>
<Image src={item.image} wrapped ui={false} />
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
<p>Date Received: {item.dateReceived.toLocaleDateString()}</p>
</Card.Description>
</Card.Content>
</Card>
</div>
</Fragment>);
}
However, I'm getting an error saying that "item" is null in MyItem.js, while the list of items is definitely not null. Similarly, when I pass in the number 4 to the "example" prop, and I do "console.log(example)" it prints out the empty object "{}".
I should note that my page previously did display everything when both pieces of code were combined on the same page (i.e. there was no "MyItem" component). The reason I decided to make it a component was because I'm iterating over this map on multiple pages and it would be redundant code. This way, I can just call <MyItem item{_item} /> and it will display the same thing on every page that shows these items.
However, now that I have refactored my code to place the mapping inside of the component, it is no longer working. What am I doing wrong here? Is this even possible, or should I just go back to what I had before?
As mentioned in the above comment:
const MyItem = ({example, item})
This solves the problem.
I need to replicate a component "n" times. To do that I used the lodash method times. The problem is that I need an index as a key for the components generated and It doesn't look like it has one.
I have the following code:
export const MyComponent: React.FC<{times: number}> = ({ times }) => {
return (
<>
{_.times(times, () => (
//I need a key={index} in this div
<div className="bg-white border-4 border-white md:rounded-md md:p-2 content-center my-4 shadow w-full">
</div>
))}
</>
);
};
This will return the component that is inside n times.
I tried to do a method that returns the component and set an index with useState, but it goes in an infinite loop. I thought to put a big random number as a key so it is extremely difficult to get the same, but I don't like that solution. I'd like to use this method because it is clean.
So what do you think I could do to give a to the component?
It's passed to you as a function parameter:
_.times(times, (index) => (blabla))
I constantly have issues trying to use code and material-ui elements in react jsx code. Here's a code snippet:
const icols = 0;
const makeTableRow = (
x,
i,
formColumns,
handleRemove,
handleSelect) =>
<TableRow key={`tr-${i}`}>
{formColumns.map((y, k) => (
y.displayColumn ? (<TableCell key={`trc-${k}`}>{x[y.name]}</TableCell>) : null), <-comma added for next line
y.displayColumn ? (cols+=1) : null)
)}
<TableCell>
<IconButton onClick={() => handleSelect(i)} ><EditIcon fontSize='small'/></IconButton>
<IconButton onClick={() => handleRemove(i)} ><DeleteForeverIcon fontSize='small' /></IconButton>
</TableCell>
</TableRow>
I am getting a jsx parsing error, when I add this line above:
y.displayColumn ? (cols+=1) : null)
If I remove the comma at the EOL above it, I still get an error. Basically I can't get a map to exec more than one statement.
If I take out the line and the EOL comma above it, everything works but I don't get a displayed column count, which I require.
I've tried using simple if/else which I am more comfortable with, but I have NEVER been able to get if/else to work in a jsx function. I want to only create a tablecell for a column w/displayColumn flag set to true, and I want a total count of the displayed columns, so I can use it later on (cols).
Is there a way to accomplish this with an if/else statement? Then I can have more than 1 statement in the if clause. The ternary operator only allows 1 statement, and I can't find anywhere what maps limitations are.
Thanks in advance for your help!
You can do something like this. You can open the open the arrow function body in map and put return JSX and do the cols increment there. Instead of having two ternary operator checks for the same condition, we can have just one conditional statement.
<TableRow key={`tr-${i}`}>
{
formColumns.map((y, k) => {
if (y.displayColumn) {
cols += 1;
return <TableCell key={`trc-${k}`}>{x[y.name]}</TableCell>
}
return null
})
}
<TableCell>
<IconButton onClick={() => handleSelect(i)} ><EditIcon fontSize='small'/></IconButton>
<IconButton onClick={() => handleRemove(i)} ><DeleteForeverIcon fontSize='small' /></IconButton>
</TableCell>
</TableRow>
Basically I can't get a map to exec more than one statement.
You can't execute more than one expression inside a arrow function definition, instead use regular declarated functions
{formColumns.map((y, k) => {
y.displayColumn ? (cols+=1) : null;
// Return what you want to render
return y.displayColumn ? (<TableCell key={`trc-${k}`}>{x[y.name]}</TableCell>) : null
}}
There are only two types of arrow function
arrow_function = () => "i will be returned"
// This way you declare only one expression after the arrow and it is returned
and
arrow_function = () => {
// This is a regular logic function
text = "i will be" + " returned";
return text;
}
EDIT 1: Add conditionals between JSX
There are two ways i know to do it
const App = () => {
return (
<div>
<h2>First form</h2>
<FirstForm true={true} />
<hr />
<h2>Second form</h2>
<SecondForm true={false} />
</div>
)
}
const FirstForm = props => {
// This way is just a ternary conditional
return (
<div>
{props.true
? <span className="success">True condition matched</span>
: <span className="danger">False condition matched</span>
}
</div>
)
}
const SecondForm = props => {
// This way uses a anonymous function executed in runtime
return (
<div>
{(() => {
let message = "Hello";
message += " World, from an auto executed anonymous function";
return (
<span className={props.true?"success":"danger"}>{message}</span>
)
})()}
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
.success {
color: darkgreen;
}
.danger {
color: #5e181b;
}
<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="react"></div>
Ok psiro, so I looked at your examples, which all work in your situation. However, as soon as I apply one to my scenario, I still get a syntax error. Like here:
<TableBody>
{(() => {
console.log('data = ' + JSON.Stringify(data, null, 2) + '.'));
return ((data.length > 0) ? (
data.map((x, i) => row(
x,
i,
formColumns,
handleRemove,
handleSelect,
editIdx
))) : (<TableRow><TableCell colSpan={`${cols}`}>No Data</TableCell></TableRow>) )
})()}
</TableBody>
And this is the problem I have w/anonymous functions. They create unmaintainable code. I am not doing anything different than your example. Just TWO js statements inside the code you presented. Your code works, mine returns a syntax error.
And why do I even NEED a double-anonymous function to have 2 simple js statements inside an if statement? Why does your code work, and mine not?
Addendum:
Ok, so I made some inferences based on your code and realized I had added yet ANOTHER anonymous function (the map statement) into the code. I reworked it into this, which compiled:
{(() => {
console.log('data = ' + JSON.Stringify(data, null, 2) + '.');
if (data.length > 0) {
return (data.map((x, i) => row(x, i, formColumns, handleRemove, handleSelect, editIdx)))
}
return(<TableRow><TableCell colSpan={`${cols}`}>No Data</TableCell></TableRow>)
})()}
The fact that it looks completely unmaintainable is irrelevant I guess. But it doesn't matter because it STILL doesn't work! Now I get a 'JSON.stringify is not a function' at runtime, which is ridiculous of course. Why can't I get a simple console.log to work in reactjs?
ADDENDUM:
Ok, I fixed the issue thanks to all the help. For anyone else that has an issue w/multiple statements inside an anonymous function, if you want to do it, you need to add a return statement so the function knows what result to return.
<TableBody>
{(() => {
console.log('data = ' + data + '.');
if (data.length > 0) {
return (data.map((x, i) => row(x, i, formColumns, handleRemove, handleSelect, editIdx)))
}
return(<TableRow><TableCell colSpan={`${cols}`}>No Data</TableCell></TableRow>)
})()}
</TableBody>
That includes when you have an anonymous function inside another anonymous function. Hope this helps anyone else having this problem.
I have this redux selector I've been working on in my React application. I've made good progress but I've hit a wall with this last issue i'm working on and I feel like it shouldn't be that difficult to solve but I'm struggling.
What I'm trying to achieve is after each item has been mapped, the next item must go to a new line.
export const vLineRejectionSelector = createSelector(
selectedVIdSelector,
linesSelector,
(id, lines) =>
lines
.filter(line => line.id === id)
.map(
(rejectString, index) =>
`Line: ${index + 1} ${rejectString.rejectReason}`
)
);
The only relevant code to look at in this is the map function. I want each item to go to a new line as its being mapped.
The output should look something like:
Line 1: Reject Reason One
Line 2: Reject Reason Two
Instead the output looks like:
Line1: Reject ReasonOneLine2: Reject Reason Two
This is being rendered in JSX as well
The value of this is passed around as a prop and gets rendered in the JSX like:
<Typography variant="body2">
{rejectReason}
{reasons && reasons.join(', ')}
</Typography>
Its value is {rejectReason}.
I would advise against composing JSX in your selector and rather just return the lines as an array as you are doing currently, but then map it to either a list or a simple <br /> joined list in the render() function. This keeps your selector more easily testable and also doesn't mix state selection concerns with presentational concerns.
E.g:
in your container
const mapStateToProps = (state) => {
return {
rejectReason: vLineRejectionSelector(state)
}
}
const SomeComponentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(SomeComponent)
export default SomeComponentContainer
and then in your SomeComponents render function:
<Typography variant="body2">
{this.props.rejectReason.map((rejectReason) => <>Line: {index + 1} {rejectReason}<br /></>)}
</Typography>
Try to use <br /> tag at the end of each line.
Like Line: ${index + 1} ${rejectString.rejectReason}<br />