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.
Related
I am new to react and I am using a carbon react library I am just wondering how can I put an if statement in map function.
It just writes the if statement in like it is normal string.
<StructuredListBody>
{matchingResults?.map((X, i) =>
<StructuredListRow}>
{if (X?.status.includes(Working)){
<StructuredListCell>{X?.status}</StructuredListCell>}
else
{<StructuredListCell>{X?.checking}</StructuredListCell>}};
</StructuredListRow>
)}
</StructuredListBody>
--- additional information
I simplified the code but I am looking at cant use a ternary operator because there will be more than 2 conditions.
It is more like this I still don't get how to return a specific component in a component. I am trying to change the color. The only thing that came to mind is to use the same again
<StructuredListBody>
{matchingResults?.map((X, i) =>
<StructuredListRow key={`row-${row.id} -${i}`}>
if (X?.status.includes(Working) {
<StructuredListCell key={`cell-${row.id}} noWrap style={{paddingBottom: '.5rem', color: 'red'}}>
{X?.status}
</StructuredListCell>}
else
{<StructuredListCell key={`cell-${row.id} noWrap style={{paddingBottom: '.5rem', color: 'yellow'}}>
{X?.status}
</StructuredListCell>}
)}
</StructuredListBody>
There are a couple of ways to have if statements in the render part of a react component. You can conditionally render different components based on some logic like below:
<StructuredListBody>
{matchingResults?.map((X, i) => {
if (X?.status.includes(Working)) {
return (
<StructuredListRow>
<StructuredListCell>{X?.status}</StructuredListCell>
</StructuredListRow>
);
} else {
return (
<StructuredListCell>{X?.checking}</StructuredListCell>
);
}
})}
</StructuredListBody>
OR you could do the logic inside the component:
<StructuredListBody>
{matchingResults?.map((X, i) => (
<StructuredListCell>
{X?.status.includes(Working) ? X?.status : X?.checking}
</StructuredListCell>
))}
</StructuredListBody>
As the only difference in rendering is the content in the StructuredListCell component. The easiest way for your case would be to use the ternary operator (?), additionally the key props need to be unique, I used the index here, but you should use something unique in your data structure.
<StructuredListBody>
{matchingResults.map((X, i) => (
<StructuredListRow key={i}>
<StructuredListCell>{X?.status.includes(Working) ? X?.status : X?.checking}</StructuredListCell>
</StructuredListRow>
))}
</StructuredListBody>
Read more about Conditional Rendering in React
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 some part of Html only if one of the variable is true. I have seen examples where I can return the whole element but I only want a if condition on one part of the html. I only want to show with 10 lines if one of the variables is true but html with 500 lines is common. Can I do that in return function?
const getCustomers= (props) => {
useEffect(() =>{ do something...});
return (
<>
if(test === true){
<div> 10 lines</div>
else
{
do not show this div
}
}
<div> 500 lines</div> // Common
</>
)
};
Conditional rendering is only supported using ternary operator and logical and operator:
{
something ? '10 lines' : '500 lines'
}
{
something && '10 lines' || '500 lines'
}
if-else statements don't work inside JSX. This is because JSX is just syntactic sugar for function calls and object construction.
For further details, you may read this, and the docs
Try to avoid logic inside of your return statements.
You can assign your conditional output to a JSX value, and always render it.
const Customers = () => {
const optionalDiv = test === true && <div>10 lines</div>;
return (
<>
{optionalDiv}
<div>500 lines</div>
</>
);
};
you can use conditional (ternary) operator
return (
<>
{ test === true ? (<div> 10 lines</div>) : null }
<div> 500 lines</div>
</>
)
I think just doing it like this should do it -
return (
<>
{test? <div> 10 lines</div> : null}
<div> 500 lines which are common</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 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>
);
}