#State() open: boolean = false;
...
...
private toggleSelect(source: string) {
console.log('fired ' + source);
this.open = !this.open;
console.log('toggleSelect: Open is now ' + this.open);
this.toggle.emit({ visible: this.open });
}
private handleSelect(value: string | undefined) {
this.value = value;
console.log('EMITTING ' + this.value);
this.changeValue.emit(this.value);
}
render() {
return (
<Host>
<button
class={`select ${this.open ? 'icon-open' : 'icon-closed'} ${
this.open ? 'select-open' : 'select-closed'
}`}
onClick={() => this.toggleSelect('button')}
>
<a> {this.getClosedDisplayValue(this.value)} </a>
<div class={`select-panel ${this.open ? 'open' : 'close'}`}>
{[{ value: 'undefined', name: '' }, ...this.data].map((item) => {
{
return (
<label class="checkmark" htmlFor={item.value}>
{this.getDisplayValue(item.value)}
<input
id={item.value}
title={this.getDisplayValue(item.value)}
name="select"
type="radio"
value={item.value}
onInput={(event) =>
this.handleSelect((event.target as HTMLInputElement).value)
}
/>
</label>
);
}
})}
</div>
</button>
<div
class={`overlay ${this.open ? 'open' : 'close'}`}
role="button"
onClick={() => this.toggleSelect('overlay')}
></div>
</Host>
);
}
}
I have the following code and what happens is when I open the drop down and select an item - I get this in the logs
*/opening the drop down
select.entry.js:39 fired button
select.entry.js:39 toggleSelect: Open is now true
*/selecting an option notice the double click event
select.entry.js:37 fired button
select.entry.js:39 toggleSelect: Open is now false
select.entry.js:37 fired button
select.entry.js:39 toggleSelect: Open is now true
select.entry.js:44 handling select was: true now is **FALSE**
is there a way to prevent this from being called twice or is because I have a input radio tag inside the button tag
I would ideally not want to have to hard code a this.open =false on the handle select method
This is a feature of React strict mode. React helps you detect side effects by running some functions twice. take a look: Why is console.log logging twice in react js?
Related
I am having an ADD button which allows me to add some objects to the option that I selected in the dropdown. And I also have a function attached to that Add Button.
onAddClicked = () =>{
this.setState({
showOptions: true
});
}
<button type="button" onClick={this.onAddClick}
style={{ float: "right", marginLeft: "10px", marginRight: "10px" }}
id="AddSelectedTag"
className={((this.state.selectedOptions ?this.state.selectedOptions.length === 0 : true) ?
"re-btn-primary-inactive" : "re-btn-primary-blue")}disabled={(this.state.selectedOptions ?
this.state.selectedOptions.length === 0 : true) ? true : false}>Add
</button>
I can select and option and few objects under it. Now I need to display an error message when I want to change my option after selecting few objects under that. I am having state for my selected options as "this.state.selectedOptions".
I am trying
if(this.state.selectedOptions ? this.state.selectedOptions.value : null)
{
alert("selectedOptions value has changed");
}
But this is returning an alert message everytime I click on the Add button, not when I try to change the state.
onViewAllTagClick = (event) => {
this.setState({
showTagsSysController: true,
selectedOptions: event,
});
}
<li id="viewAllTags" className="re-exp-pak-edit-system-li">
<div className="re-lbl-normal-12">
<Select id="selectTags"
styles={ddlUtils.getSnapDdlStyle(200)}
placeholder="Select Tag, Network or Hardware "
options={[{label:"Tag",value:2,isChild:0},
{label:"Network",value:5,isChild:0},
label:"Hardware",value:3,isChild:0},
]}
value = { this.state.selectedOptions}
isSearchable={false}
isDisabled={this.state.isEdit === true}
noOptionsMessage={() => null}
onChange = {this.state.selectedSystems !== null ? this.onViewAllTagClick : null}>
</Select>
Add an event listener like componentDidUpdate and compare props
componentDidUpdate(prevProps) {
if(this.props.selectedOptions !== prevProps.selectedOptions)
alert("selectedOptions value has changed");
}
I am using a json with several values, one of them is "iframe" which can be "si" (yes) or "no" depending on whether it is an iframe or not.
With that value (yes / no) I need (this.props.tabsiframe === 'yes') to show an <iframe> or a <div>.
My code works, because if (this.props.tabsiframe === 'yes') is an iframe it paints an iframe, but if the next element is (this.props.tabsiframe === 'no' ) change all the elements of <iframe> to <div>.
The re-rendering changes the already created divs and converts them to iframe if iframe = yes and if it is = it does not change the iframe to div. This is an example of what happens
https://drive.google.com/file/d/1cY9eHq_aBuQb8b_HtYUXjC6lqrYCJe3p/view
Maybe it happens because every time I click on the element the menu is updated: divIframe: {tabsDivIframe: [... new Set (this.state.divIframe.tabsDivIframe), url] .filter (function (el) { return the;})},. A new url is added to the array, so I guess that's what it renders and it changes the divs to iframes or iframes to divs.
class App extends Component {
constructor(props, context){
super(props, context);
["openTabs", "removeTab"].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
tabs:{
tabsLi: [],
},
divIframe:{
tabsDivIframe: [],
},
tabsiframe : '',
showtabs: true,
}
}
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
this.setState({
showtabs: false,
})
if (this.state.tabs.tabsLi.includes(trdtitle) === false){
this.setState({
tabs: { tabsLi:[...new Set(this.state.tabs.tabsLi),trdtitle].filter(function(el) { return el; })},
divIframe: { tabsDivIframe:[...new Set(this.state.divIframe.tabsDivIframe),url].filter(function(el) { return el; })},
tabsiframe: iframe,
}, () => {
console.log(this.state.tabs.tabsLi);console.log(this.state.divIframe.tabsDivIframe);console.log(this.state.tabsiframe)
})
}
}
render(){
return (
<><Tabs
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
divIframe={this.state.divIframe}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
tabsiframe={this.state.tabsiframe}
openTabs={this.openTabs}
removeTab={this.removeTab}
/>
</>
)
}
}
class Tabs extends Component {
render(){
return(
<div id="content-tabs" className="tabs">
{( this.props.showtabs)
? (
<>
<div className="waiting-leads">
<p>Parece que todavía no hay ningún lead...</p>
<h3>¡Ánimo, ya llega!</h3>
<img src={imgDinosaurio} alt="Dinosaurio"></img>
</div>
</>
) : (
<>
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
tabsiframe={this.props.tabsiframe}
/>
</>
)}
</div>
);
}
}
class DivAndIframe extends Component{
render(){
return(
<>
{this.props.tabsDivIframe.map((url, index) =>
<div key={url.toString() id={"myTab" + index}>
{( this.props.tabsiframe === 'si')
? (
<iframe title={"iframe"+index} className="iframeTab" src={url}></iframe>
) : (
<div>{url}</div>
)}
</div>
)}
</>
);
}
}
The problem you are having here is within your map.
Here is a state map to try to explain:
After First tab Click (div):
this.props.tabsiframe = 'no'
this.props.tabsDivIframe = ['www.sampleurl1.com']
After Second tab Click (iframe):
this.props.tabsiframe = 'yes'
this.props.tabsDivIframe = ['www.iframe2.com', 'www.sampleurl1.com']
so on the virtual DOM after the second click, the map will again iterate through this.props.tabDivIframe and evaluate this.props.tabsiframe as 'si' or 'no' for everything.
{this.props.tabsDivIframe.map((url, index) =>
<div key={url.toString() id={"myTab" + index}>
{( this.props.tabsiframe === 'si') //this line here; we share this same value for all tabs
? (
<iframe title={"iframe"+index} className="iframeTab" src={url}></iframe>
) : (
<div>{url}</div>
)}
</div>
)}
I wrote a small interactive sample of your problem here: https://jsfiddle.net/eojrx6my/6/
You will need to store individual 'tabsiframe' for each url, or somehow identify them individually
I'm creating an application where a user can search for a device with a search bar, or look through a nested menu. When the user finds the device they want, they click on the green plus to add it to their "bag". For some reason the addDevice function I've written only works for the search function, and not when it's called in the menu. It seems to partially work, but not correctly. Does anyone know what in my code could be causing this? Please see the images below for more details.
I'm also making two different API calls, one for the search bar (called in its own function) and one for the menu (called in componentDidMount). Could this possibly be what's causing the error?
I won't include every line of code because it's a lot, but please let me know if you'd like to see anything else.
class App extends React.Component {
state = {
devices: [],
bag: [],
objectKeys: null,
tempKeys: []
};
this is the function that gets called by the onClick's
addDevice = (e, deviceTitle) => {
const array = Array.from(this.state.bag || []);
if (array.indexOf(deviceTitle) === -1) {
array.push(deviceTitle);
} else {
return;
}
localStorage.setItem("list", JSON.stringify(array));
this.setState({
bag: array
});
};
This is the function that doesn't seem to work when addDevice is called
makeMenuLayer = layer => {
const { objectKeys } = this.state;
if (layer == null) {
return null;
}
const layerKeys = Object.entries(layer).map(([key, value]) => {
{/*If value has children, display an arrow, if not, do nothing*/}
var arrow = Object.keys(value).length ? (
<i
className="fas fa-angle-right"
style={{ cursor: "pointer", color: "gray" }}
/>
) : (
""
);
{/*If value has no children, display an plus sign, if not, do nothing*/}
var plus =
Object.keys(value).length === 0 ? (
<i
className="fas fa-plus"
style={{ cursor: "pointer", color: "green" }}
onClick={e => this.addDevice(e, this.value)}
/>
) : (
""
);
return (
<ul key={key}>
<div onClick={() => this.handleShowMore(key)}>
{key} {arrow} {plus}
</div>
{objectKeys[key] && this.makeMenuLayer(value)}
</ul>
);
});
return <div>{layerKeys}</div>;
};
this is where the addDevice that does work gets called
render () {
return(
<div className="search-results">
{(this.state.devices || []).map(device => (
<a key={device.title}>
<li>
{device.title}{" "}
<i
className="fas fa-plus plus input"
style={{ cursor: "pointer", color: "green" }}
onClick={e => this.addDevice(e, device.title)}
/>
</li>
</a>
))}
</div>
)}
This is what it looks like when devices are added from the search (works fine)
This is what happens when I try to add "Necktie" from the menu. It doesn't let me add anything else after that
In this line onClick={e => this.addDevice(e, this.value)}
The value of this points to the class itself. Thus, this.value is undefined, It's not not allowing you to add anything more because undefined is already in the array and undefined === undefined is actually true.
To fix this, you need to pass the correct value of the device title.
I am working on a project and i want to display a hidden <div> below another <div> element using an event handler but when i click the icon that is meant to display the div, the whole page becomes blank
This is image I want:
This is what i get
I have tried to check through the internet for some places where i could get the solution. Well i found something similar to what i had done but the error still happens for me.
class PostItTeaser extends Component {
state = {
postIt: false,
moreIt: false,
}
togglePostIt = e => {
e ? e.preventDefault() : null
this.setState({ postIt: !this.state.postIt })
}
_toggle = e => {
e ? e.preventDefault() : null
this.setState({
moreIt: !this.state.moreIt,
})
}
Child = () => <div className="modal">Hello, World!</div>
render() {
let { postIt } = this.state
let { moreIt } = this.state
let {
type,
group,
disabled,
session: { id, username },
} = this.props
return (
<div>
<div
className="post_it inst"
style={{ marginBottom: type == 'group' && 10 }}
>
<img src={`/users/${id}/avatar.jpg`} alt="Your avatar" />
<div className="post_teaser">
<span
className="p_whats_new"
onClick={disabled ? null : this.togglePostIt}
>
What's new with you, #{username}? #cool
</span>
<span className="m_m_exp" data-tip="More" onClick={this._toggle}>
<MaterialIcon icon="expand_more" />
</span>
</div>
</div>
{moreIt && <Child />}
{postIt && (
<PostIt back={this.togglePostIt} type={type} group={group} />
)}
</div>
)
}
}
From skimming through the code I believe you need to bind the scope, since the function you're calling is using this.setState, it needs this to be the react component, not the event you're listening to:
onClick={this._toggle.bind(this)}
You can also bind the functions scope in the constructor. Or, a less memory performant & ugly way:
onClick={() => { this._toggle(); } }
I am working on a component where I need to display and hide a modal.
this is what I have in the render method in React
<div style={{visibility : this.state.displayModal}}>
<p>Pop up: Bet Behind Settings</p>
</div>
<button onClick={this._openModal}>CLICK</button>
and here is the function
_openModal = () => {
if (this.state.displayModal === 'hidden') {
this.setState({
displayModal : 'visible',
})
} else {
this.setState({
displayModal : 'hidden',
})
}
}
the main concern I have, is, how to set the state in a more elegant way, or this should be the way to do it ?
here the full code
constructor (props) {
super(props);
this.state = {
displayModal : 'hidden',
}
}
render () {
return (
<div style={{visibility : this.state.displayModal}}>
<p>Pop up: Bet Behind Settings</p>
</div>
<button onClick={this._openModal}>CLICK</button>
)
}
_openModal = () => {
if (this.state.displayModal === 'hidden') {
this.setState({
displayModal : 'visible',
})
} else {
this.setState({
displayModal : 'hidden',
})
}
}
so, what should be the way to this pop up in a React way.
I think it's a good way to do it. But it will be more concise if you make displayModel a boolean:
_toggleModal = () => this.setState({displayModal: !this.state.displayModal})
On a complex page using hidden will be a performance issue. Try something like this instead;
render() {
var returnIt;
if (this.state.hide) {
returnIt = null;
} else {
returnIt = (
<div style={{visibility : this.state.displayModal}}>
<p>Pop up: Bet Behind Settings</p>
</div>
<button onClick={this._openModal}>CLICK</button>
)
}
return (returnIt);
}
This is just a personal opinion, but I think a better UX would be that the button should only be used to open the modal; and the modal should be closed by either clicking the X in the modal (if there is) or when you click anywhere outside the modal.
That said if you definitely need the button to toggle between the 2 states, how about something like this?
constructor (props) {
super(props);
this.state = {
displayModal : false
}
}
render () {
return (
<div style={{visibility : this.state.displayModal === true ? 'visible' : 'hidden'}}>
<p>Pop up: Bet Behind Settings</p>
</div>
<button onClick={this._toggleModal}>CLICK</button>
)
}
_toggleModal = () => {
const current = this.state.displayModal;
this.setState({
displayModal : !current
});
}
Using https://github.com/fckt/react-layer-stack you can do like so:
import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
return (
<Cell {...props}>
// the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
<Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
hideMe, // alias for `hide(modalId)`
index } // useful to know to set zIndex, for example
, e) => // access to the arguments (click event data in this example)
<Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
<ConfirmationDialog
title={ 'Delete' }
message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
confirmButton={ <Button type="primary">DELETE</Button> }
onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
close={ hideMe } />
</Modal> }
</Layer>
// this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
<LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
<div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
<Icon type="trash" />
</div> }
</LayerContext>
</Cell>)
// ...