Having some trouble with state management in react-sound. I have a dynamic grid of buttons - when mousing over a button, audio plays as an example of what that button represents, and when mousing off, it stops. Right now, it's running into problems when you click on a button and the screen reloads another array as a grid of buttons - when you do that and then navigate back to the former screen, the behavior is REVERSED, so that audio plays when you mouse OFF the button and stops when you mouse ON. I've tried several things that I thought should work, including triggering toggleHoverState() on click, but nothing has worked so far. Any advice? Code below:
class Screen extends React.Component {
constructor(props) {
super(props);
this.state = {displayArray: taxonomyArray,
isHovering: false};
this.handleClick = this.handleClick.bind(this);
this.handleMouseHover = this.handleMouseHover.bind(this);
}
handleMouseHover() {
this.setState(this.toggleHoverState);
}
toggleHoverState(state) {
return {
isHovering: !state.isHovering,
};
}
handleClick(updatedArray = taxonomyArray) {
//this.setState(this.toggleHoverState);
this.setState({
displayArray: updatedArray,
});
}
render () {
return (
this.state.displayArray.map(item =>
<Col key={Math.random()} span={4} xs="auto" sm="auto" md="auto" lg="auto">
<a key={Math.random()} data-tip={item.description ? (item.description) : console.log('null description')}>
{ (item.audio && this.state.isHovering) && <div><Sound url={item.audio} playStatus={Sound.status.PLAYING} volume={50}/></div> }
<Button
className='trigger'
key={Math.random()}
variant= {item.child === undefined ? "outline-secondary" : (item.child === null ? "secondary" : "primary") }
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
onClick={ () => ( item.child === null ? console.log('nope') : ( item.child === undefined ? this.handleClick(arrayVisits.pop()) : (arrayVisits.push(this.state.displayArray), this.handleClick(item.child))) ) }
>
{item.name}
</Button>
</a>
<ReactTooltip className='extraClass' delayHide={0} effect='solid' type="info" multiline={true}/>
<div> </div>
</Col>
)
);
}
}
This logic here is very strange:
handleMouseHover() {
this.setState(this.toggleHoverState);
}
toggleHoverState(state) {
return {
isHovering: !state.isHovering,
};
}
Why not just write
handleMouseHover() {
this.setState({ isHovering: !this.state.isHovering })
}
But if you're still having problems, I would write some very explicit functions for onMouseEnter and onMouseLeave to reduce confusion:
onMouseEnter={ () => { this.setState( isHovering: true ) } }
onMouseLeave={ () => { this.setState( isHovering: false) } }
Let me know if that solves it
Is the page actually reloading, or is it a BrowserRouter?
It sounds like, if it's a BrowserRouter, that state isn't being reset. Try adding:
componentDidMount () {
this.setState({isHovering:false})
}
Related
I am trying to implement a textbox similar to google flights.
so I have built a react autocomplete prototype.
but in that i am facing an issue.
right now in the google flights textbox when I click on the textbox it shows all the rersults without typing anything.
but in my case if I type something only it will show the results.
so the textbox I added a props onPress in that I am calling an event handleEvent.
but nothing printing inside the method.
can you tell me how to achieve so that in future I will fix it myself.
providing my code snippet and sandbox below
https://codesandbox.io/s/xp6x167kq4
handleEvent = () => {
console.log("I was clicked");
alert("I was clicked");
};
render() {
const {
onChange,
onClick,
onKeyDown,
state: {
activeSuggestion,
filteredSuggestions,
showSuggestions,
userInput
}
} = this;
let suggestionsListComponent;
if (showSuggestions && userInput) {
if (filteredSuggestions.length) {
suggestionsListComponent = (
<ul class="suggestions">
{filteredSuggestions.map((suggestion, index) => {
let className;
// Flag the active suggestion with a class
if (index === activeSuggestion) {
className = "suggestion-active";
}
return (
<li className={className} key={suggestion} onClick={onClick}>
{suggestion}
</li>
);
})}
</ul>
);
} else {
suggestionsListComponent = (
<div class="no-suggestions">
<em>No suggestions, you're on your own!</em>
</div>
);
}
}
return (
<Fragment>
<input
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
value={userInput}
onPress={this.handleEvent}
//onPress={this.handleEvent}
/>
{suggestionsListComponent}
</Fragment>
);
}
I believe this is because in your initial state you've written
this.state = {
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: '',
};
and later you run
if (showSuggestions && userInput) but on an initial click userInput still equals '' which equates to false. Underneath again, you run if (filteredSuggestions.length) which also equates 0 because when nothing is typed the array filteredSuggestions is empty.
console.log('' == true) => false also
console.log([].length == true) => false
I'm trying to toggle two different dropdown menus and can't seem to get it working. New to react and have probably been looking at it too long and it's something simple. The problem is when I toggle one the other gets toggled as well, so they both show.. Here is what I have:
import React from "react";
import { Link } from "./component/link";
import styles from "./header.module.css";
class Header extends React.Component {
constructor(props) {
super ( props )
this.state = {
show : false
}
this.toggleBusiness = this.toggleBusiness.bind(this);
this.state = {
show : false
}
this.togglePersonal = this.togglePersonal.bind(this);
}
toggleBusiness = () => {
const { show } = this.state;
this.setState( { show : !show } )
}
togglePersonal = () => {
const { show } = this.state;
this.setState( { show : !show } )
}
render() {
return (
<div className={ styles.topNav} >
<div className="grid">
<div className="grid-cell">
<div className={ styles.logoText }>
Logo
</div>
</div>
<nav>
<div className="grid-cell">
<ul className="list-unstyled">
<li><Link to={'/design'}>About</Link></li>
<li><a onClick={this.toggleBusiness}>Business</a></li>
<li><a onClick={this.toggleBusiness}>Personal</a></li>
<li><Link to={'/posts'}>Blog</Link></li>
<li><Link to={'/contact'}>Contact</Link></li>
<li className={styles.menuButton}><a className="button button-secondary" href="tel:2252931086">File a Claim</a></li>
<li className={styles.menuButton}><a className="button" href="/">Get Insurance</a></li>
</ul>
</div>
</nav>
</div>
{ this.state.show && <BusinessDropdown /> }
{ this.state.show && <PersonalDropdown /> }
</div>
)}
}
class BusinessDropdown extends React.Component {
render () {
return (
<div className="dropdown">BusinessTest</div>
)
}
}
class PersonalDropdown extends React.Component {
render () {
return (
<div className="dropdown">PersonalTest</div>
)
}
}
export default Header;
So basically I want it to toggle the Business Dropdown one when I click Business and the Personal Dropdown when I press Personal. Also, if you have something that would work better than this approach let me know!
Change your toggleBusiness and togglePersonal to this:
toggleBusiness = () => {
const { show } = this.state;
this.setState({ show: show === "business" ? null : "business" });
};
togglePersonal = () => {
const { show } = this.state;
this.setState({ show: show === "personal" ? null : "personal" });
};
then in the conditional rendering, do this:
{this.state.show === "business" && <BusinessDropdown />}
{this.state.show === "personal" && <PersonalDropdown />}
...also in your links, you have this:
<li><a onClick={this.toggleBusiness}>Business</a></li>
<li><a onClick={this.toggleBusiness}>Personal</a></li>
Where you should have this:
<li><a onClick={this.toggleBusiness}>Business</a></li>
<li><a onClick={this.togglePersonal}>Personal</a></li>
Edit: I realise this is not quite what you asked for - this toggles business off when personal is switched on. Personally I think this approach would actually suit better given the fact you're opening a new dropdown menu, you will probably want the other one to close.
You are using this.state.show for both Business and Personal.
Adding a second state variable like showBusiness and showPersonal
should solve your problem.
Also you can/should declare your state only once with
this.state = {
showBusiness: false,
showPersonal: false
};
In your constructor you set this.state.show to false 2 times separate this into two separate variables, perhaps this.state.showBuisness and this.state.showPersonal?
My approach would be something like this.
1. set initial state
constructor(props){
super(props);
this.state={
business: false,
personal: false
}
}
2. Create ONE function to update both status.
handleClick = (e) => {
this.setState(prevState => {
[e.target.id]: !prevState[e.target.id]
}
}
3. Add function to the onclick AND an id
<button id="personal" onClick={this.handleClick}>SHOW PERSONAL</button>
As part of a technical test, I've been asked to write an autocomplete input in React. I've done this but I'd now like to add the functionality of navigating up and down the rendered list with the arrow keys. I've done some extensive Googling and found nothing React specific apart from npm packages.
To be clear, I'm looking for something like this but for React: https://www.w3schools.com/howto/howto_js_autocomplete.asp
All I basically need is the arrow button functionality, I've got everything else working fine.
Cheers
Here's an example that I tried but couldn't get working.
export default class Example extends Component {
constructor(props) {
super(props)
this.handleKeyDown = this.handleKeyDown.bind(this)
this.state = {
cursor: 0,
result: []
}
}
handleKeyDown(e) {
const { cursor, result } = this.state
// arrow up/down button should select next/previous list element
if (e.keyCode === 38 && cursor > 0) {
this.setState( prevState => ({
cursor: prevState.cursor - 1
}))
} else if (e.keyCode === 40 && cursor < result.length - 1) {
this.setState( prevState => ({
cursor: prevState.cursor + 1
}))
}
}
render() {
const { cursor } = this.state
return (
<Container>
<Input onKeyDown={ this.handleKeyDown }/>
<List>
{
result.map((item, i) => (
<List.Item
key={ item._id }
className={cursor === i ? 'active' : null}
>
<span>{ item.title }</span>
</List.Item>
))
}
</List>
</Container>
)
}
}
And here is my code:
class Search extends Component {
constructor(props) {
super(props);
this.state = {
location: '',
searchName: '',
showSearch: false,
cursor: 0
};
}
handleKeyPress = e => {
const { cursor, searchName } = this.state;
// arrow up/down button should select next/previous list element
if (e.keyCode === 38 && cursor > 0) {
this.setState(prevState => ({
cursor: prevState.cursor - 1
}));
} else if (e.keyCode === 40 && cursor < searchName.length - 1) {
this.setState(prevState => ({
cursor: prevState.cursor + 1
}));
}
};
render() {
const { searchName, location } = this.state;
return (
<div className="Search">
<h1>Where are you going?</h1>
<form id="search-form" onSubmit={this.handleSubmit}>
<label htmlFor="location">Pick-up Location</label>
<input
type="text"
id="location"
value={location}
placeholder="city, airport, station, region, district..."
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}
onKeyDown={this.handleKeyPress}
/>
{this.state.showSearch ? (
<Suggestions searchName={searchName} />
) : null}
<button value="submit" type="submit" id="search-button">
Search
</button>
</form>
</div>
);
}
Code that renders the list from the restful API:
.then(res =>
this.setState({
searchName: res.data.results.docs.map(array => (
<a href="#">
<div
key={array.ufi}
className="locations"
>
{array.name}
</div>
</a>
))
})
);
Since you are defining a function as handleKeyDown(e) {...} the context this is not pointing to the context of class instance, the context will be supplied by onKeyDown (and I suppose it's window as this)
So, you have 2 ways to go:
declare your function as handleKeyDown = (e) => {...}
bind handleKeyDown context to component instance like onKeyDown={this.handleKeyDown.bind(this)}
Also, don't forget that you may want a mod items.length counter, meaning when your press down and the last item is already selected, it would go to the first item.
Your api usage with storing markdown into the state is just a terrible thing to do. You don't have access to these strings anymore, instead save it as a plain array. Pass it where you need it, and use it to create jsx there.
Also, you don't use cursor from your state at all.
I have a button. If you click it, the text changes (due to setState({})). I want to (only if) the button was clicked and the text changes to pop up a modal component. The function for modal pop up again changes states. The modal should also pop up 1-3 sec after the change. I tried using timeout(function...) but that didn't work. Calling 2 functions only works for text change but not for the modal popup. Any help would be amazing!
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
display: false,
modal: false
}
this.onClick = this.onClick.bind(this);
}
change() {
this.setState({
display: !this.state.display
})
};
toggle() {
if (this.state.modal == true) {
this.setState({
modal: !this.state.modal
})
}
};
onClick() {
this.change()
this.toggle()
}
render() {
if (this.state.display) {
return <a onClick={() => { change(); toggle();}}><p>Hello</p> </a>
<Modal onClick={this.onClick} status={this.state.modal}/>
} else {
return <a onClick={this.onClick}> <p>Bye</p></a>
}
}
}
Insight my Modal component:
....
return(
<div className="modal" data-status={this.props.status}>
....
If all of this logic must exist inside the component, one place to check for state changes is the componentDidUpdate() lifecycle function. If I understand your intention correctly, the code could instead look like the following:
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
display: false,
modal: false
}
this.toggleDisplay = function(){
this.setState({
display: !this.state.display
});
}.bind( this );
this.showModal = function(){
this.setState( { modal: true } );
}.bind( this );
this.hideModal = function(){
this.setState( { modal: false } );
}.bind( this );
}
componentDidUpdate( prevProps, prevState ){
// if toggled to display
if( !prevState.display && this.state.display ){
if( !this.state.modal ){
setTimeout( this.showModal, 3000 ); //delay 3 seconds
}
}
// if toggled to not display
else if( prevState.display && !this.state.display ){
if( this.state.modal ){
this.hideModal(); //assuming you don't want to delay hide
}
}
}
render() {
const msg = this.state.display ? 'Hello' : 'Bye',
modal = this.state.modal ? (
<Modal onClick={this.toggleDisplay} status={this.state.modal}/>
) : null;
return (
<div>
<a onClick={this.toggleDisplay}}><p>{msg}</p></a>
{modal}
</div>
);
}
}
As I have it, it will be prone to error if state changes occur before the execution of the timer, but I'll leave that problem as an exercise for you.
I want to change the text of a button when I press it.
This is what I want to achieve: https://codepen.io/gaearon/pen/xEmzGg?editors=0010 , but I can't get my code to work (newbie).
class ObjectKeyDisplay extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(prevState => ({
open: prevState.open
}))
}
renderInner() {
if (!this.props.value) return <td className = "inactive" > < /td>
if (!this.state.open && this.props.schema.type === 'belongs_to')
return <td onClick={(e) => this.setState({open: !this.state.open})}>
<button onClick={this.handleCLick}>
{this.state.open ? 'OFF' : 'ON'}
</button>
</td>
}
Here's my code and as you might have seen I want to toggle the text OFF and ON when pressing the button. I can add more code if needed. Really grateful for all the support I can get.
Here is what you are looking for:
handleClick() {
const open = !this.state.open;
this.setState({open})
}
The bug is in handler:
this.setState(prevState => ({
open: !prevState.open
}))
You are setting it to the same value, as it was before.. with !bool you can switch the value
Additionally the condition is not making any sense:
if (!this.state.open && this.props.schema.type === 'belongs_to')
return <td onClick={(e) => this.setState({open: !this.state.open})}>
<button onClick={this.handleCLick}>
{this.state.open ? 'OFF' : 'ON'}
</button>
</td>
This means only if the condition is met, you component will return something..
true === (!this.state.open && this.props.schema.type === 'belongs_to')