if we have not put null then it gives parsing error.
render() {
return (
<div className="dropdown" style = {{background:"Red",width:"2000px"}} >
<div className="button" onClick={this.showDropdownMenu}> User Info</div>
{this.state.displayMenu ? (
<ul>
<li><a className="active" href="#Orders">Orders</a></li>
<li>Payment Details</li>
<li>Your Address</li>
<li>Profile</li>
<li>Activity</li>
<li>Setting</li>
<li>Log Out</li>
</ul>
):
(
null
)}
</div>
);
}
If you don't want to add null don't use ternary, you can do this:
render() {
return (
<div className="dropdown" style = {{background:"Red",width:"2000px"}} >
<div className="button" onClick={this.showDropdownMenu}> User Info</div>
{ this.state.displayMenu && (
<ul>
<li><a className="active" href="#Orders">Orders</a></li>
<li>Payment Details</li>
<li>Your Address</li>
<li>Profile</li>
<li>Activity</li>
<li>Setting</li>
<li>Log Out</li>
</ul>
)}
</div>
);
}
You should always return a value from an expression. Even if the value it's null. To avoid ternary operators when you have a boolean you could render like this
return condition && <JSX />
For the case that this.state.displayMenu is false, you return null to preventing a component from rendering. In your case it's explicitly set to null.
React docs: https://reactjs.org/docs/conditional-rendering.html#preventing-component-from-rendering
In rare cases you might want a component to hide itself even though it was rendered by another component. To do this return null instead of its render output.
Related
I'm trying to get to work a delete btn which is inside a div which is wrapped in react-router-dom tag. I want to be able to navigate to the established path when that div is clicked. But if the target is that delete btn, then it shouldn't navigate just execute the onClick inside the delete btn. Is this possible? I couldn't figure out on my own. Thanks for reading.
<ul className="m-entries">
{
letters.map(letter => {
return (
<li className="o-entry" key={ letter.id }>
<Link to={`/Letters/letter/${ letter.id }`}>
<div className="l-entry-container">
<div className="l-entry__header">
<h2 className="c-text__h2 c-letter-title">{ letter.title }</h2>
// This is the delete btn
<button
className="c-entry__btn c-entry__dlt-btn"
onClick={(e) => {
displayConfirmDeleteWdw(e, letter.id)
}}
type="button"
>
<XDeleteBtn />
</button>
</div>
<p className="c-text__p c-letter__preview">{ letter.synopsis }</p>
</div>
</Link>
</li>
)})
}
</ul>
It's generally considered bad UI/UX to place interactive DOM elements within other interactive DOM elements (in fact some elements are sometimes illegal when used like this), but there is a way to accomplish this.
The button's onClick event should not propagate up to the Link component. Call stopPropagation on the event to prevent it from bubbling up further.
<ul className="m-entries">
{letters.map(letter => {
return (
<li className="o-entry" key={letter.id}>
<Link to={`/Letters/letter/${letter.id}`}>
<div className="l-entry-container">
<div className="l-entry__header">
<h2 className="c-text__h2 c-letter-title">{letter.title}</h2>
<button
className="c-entry__btn c-entry__dlt-btn"
onClick={(e) => {
e.stopPropagation(); // <-- prevent event from bubbling up
displayConfirmDeleteWdw(e, letter.id);
}}
type="button"
>
<XDeleteBtn />
</button>
</div>
<p className="c-text__p c-letter__preview">{letter.synopsis}</p>
</div>
</Link>
</li>
)})
}
</ul>
I am pulling project info from database and then based on how many projects I have, I push to an array and render. The problem is for some reason the "dropdownOptions" will not toggle between showing and hiding.
I basically just want to be able to click the dropdown arrow and have it show options like "delete" or "settings". Also not sure if I need to have a separate id for each project component created.
Code Snippets:
// Using hook to set display of dropdown to false
const [dropdownOptions, setDropdownOptions] = useState(false);
// This piece automatically renders clickable projects from database
const pList = [];
for (var i = 0; i < project_list.length; i += 1) {
var projectName = project_list[i]
pList.push (
<div className="projects" key={i}>
<div className="projectName">Name: <div className="projectText">{projectName}</div></div>
<div className="projectDate"> Date Created: <div className="projectText">{date}</div></div>
<div className="projectLabels">LabelsLeft:</div>
<div className="drop">
<div className="dropArrow" onClick{setDropdownOptions(!dropdownOptions)}>
{dropdownOptions ? <ProjectDropdown/> : null}
</div>
</div>
</div>
);
};
setpList(pList);
})
// Then I simply render the component "pList"
return (
<div className="container">
<div className="projectsContainer">
<div className="projectsTitle">
Projects:
<div className="addProject" onClick={toggleShow}></div>
</div>
{pList}
<div className="message">
{message}
</div>
</div>
</div>
)
Ideal look would be this before user presses droparrow on project
And this afterwards, but only for the project clicked
You have to options to such problems:
1- make a state for each item in the list, and for each one you have a boolean that can tell if that item has its dropdown opened or not
2- make single state item, but make it a number that represents the clicked on item id, when that id matches the state you open the dropdown for that item
const [selectedItem, setSelectedItem] = useState(-1);
and in your jsx
<div className="projects" key={i}>
<div className="projectName">Name: <div className="projectText">{projectName}</div></div>
<div className="projectDate"> Date Created: <div className="projectText">{date}</div></div>
<div className="projectLabels">LabelsLeft:</div>
<div className="drop">
<div className="dropArrow" onClick{()=>setSelectedItem(i)}>
{selectedItem == i ? <ProjectDropdown /> : null}
</div>
</div>
</div>
And as an advice, it's preferable to add a real id taken from your API, because the key passed to each item is so important for react, not only for showing the dropdown.
Suggested solution code
function TestSolution() {
// Using hook to set display of dropdown to false
const [selectedItem, setSelectedItem] = useState(-1);
// This piece automatically renders clickable projects from database
const pList = project_list.map((projectName, i) => (
<div className="projects" key={i}>
<div className="projectName">Name: <div className="projectText">{projectName}</div></div>
<div className="projectDate"> Date Created: <div className="projectText">{date}</div></div>
<div className="projectLabels">LabelsLeft:</div>
<div className="drop">
<div className="dropArrow" onClick{() => setSelectedItem(i)}>
{selectedItem == i ? <ProjectDropdown /> : null}
</div>
</div>
</div>
))
// Then I simply render the component "pList"
return (
<div className="container">
<div className="projectsContainer">
<div className="projectsTitle">
Projects:
<div className="addProject" onClick={toggleShow}></div>
</div>
{pList}
<div className="message">
{message}
</div>
</div>
</div>
)
}
Thinking in react is different, don't use a new state for some data if you can derive it from another state or data, that will make your life easier.
Am trying to render different tags based JSON keys, but it keeps throwing syntax error...
adding only one of the two conditions works though !
How can I do it right ?
<ul className="features">
{
property.details.features.map((idx, feature) =>
feature.hasOwnProperty(area) ?
<li>
<span className="icon-frame"/>
<div className="english">{feature[area]}m2 </div>
</li> : null
feature.hasOwnProperty(bedRoom) ? <li>
<span className="fa fa-bed"/>
<div className="english">{feature[bedRoom]}</div>
</li> :null
)
}
</ul>
The problem is you have an arrow function without braces in the body of the function, which means the expression right after the arrow will be what that function returns. But you have two expressions in a row there (the two ternary operators), so it won't know what to return, hence the syntax error. You can try this:
<ul className="features">
{
property.details.features.map((idx, feature) =>
feature.hasOwnProperty(area) ?
(
<li>
<span className="icon-frame"/>
<div className="english">{feature[area]}m2 </div>
</li>
)
: feature.hasOwnProperty(bedRoom) ?
(
<li>
<span className="fa fa-bed"/>
<div className="english">{feature[bedRoom]}</div>
</li>
)
: null
)
}
</ul>
So, if feature has the attribute area, then render the first, else, if it has the attribute bedrom, then render the second, else, return null. I tried to make it as readable as possible but it's never easy with long ternary operators.
You can always just wrap the body of the arrow function and do it with if, else and return statements.
I have been messing around with a few conditional rendering methods but, I can't seem to find one that works.
<div className="tags">
{adv_event.types.map(type => (
<div className="tag" key={type.tid}>
<h5 className="body-color">Event Type:</h5>
<Link to={`/events/category/${type.slug}`} className="home-link track-click">{type.name}</Link>
</div>
))}
</div>
Right now <h5 className="body-color">Event Type:</h5> is repeated for every tag. Is there a way to show the title once without adding it before each tag?
Move it outside the loop?
<div className="tags">
{adv_event.types.length > 0 ? (<h5 className="body-color">Event Type:</h5>) : ''}
{adv_event.types.map(type => (
<div className="tag" key={type.tid}>
<Link to={`/events/category/${type.slug}`} className="home-link track-click">{type.name}</Link>
</div>
))}
</div>
I have the following jsx code, I am trying to put some code out of the mapping function, but getting errors with the jsx syntax, I want this block displayed only if there is some data..
{this.props.someData ?
<div className="container">
<h3>Heading</h3>
<div className="block1">
<button>one</button>
<buttontwo</button>
</div>
this.props.someData.map((response, index) => {
return (
<div className="Block2">
<div key={index}>
<span>{response.number}</span>
</div>
</div>
</div>
);
}) : ''}
Write it like this:
{this.props.someData ?
<div className="container">
<h3>Heading</h3>
<div className="block1">
<button>one</button>
<buttontwo</button>
</div>
{this.props.someData.map((response, index) => {
return (
<div className="Block2">
<div key={index}>
<span>{response.number}</span>
</div>
</div>
)})
}
</div>
: <div/>}
Mistake you are doing:
To render any js code inside html elements, {} is required, since you are rendering JSX if the condition is true and inside that using map so put map function inside {}, it will work.
You should be able to add a child { wrapping your map. You have a few other errors as well. You were cosing your tags and braces too early. Additionally, an "empty" JSX element should return null, not empty string. You also have broken syntax with no closing > here: <buttontwo</button>
Using a code editor that provides syntax highlighting, and using consistent spacing, and always using eslint to check for errors, will help prevent the need to come to StackOverflow and blocking your programming on syntax questions.
{this.props.someData ?
<div className="container">
<h3>Heading</h3>
<div className="block1">
<button>one</button>
<button>two</button>
</div>
{ this.props.someData.map((response, index) => {
return (
<div className="Block2">
<div key={index}>
<span>{response.number}</span>
</div>
</div>
);
}) }
</div> : null}
If I get your meaning correctly, I think this would do:
{this.props.someData &&
(<div className="container">
<h3>Heading</h3>
<div className="block1">
<button>one</button>
<buttontwo</button>
</div>
{this.props.someData.map((response, index) => {
return (
<div className="Block2">
<div key={index}>
<span>{response.number}</span>
</div>
</div>
);
})}
</div>)
}
I'd recommend you to have a look at the conditional rendering section of the documentation.