Function is not being executed within setTimeout - javascript

I am trying to use following code with React Native:
...
_getContentHeight() {
if (this.refs.AccordionContent) {
this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => {
// Sets content height in state
this.setState({
height: this.props.expanded ? height : 0,
content_height: height
});
});
}
},
componentDidMount() {
// Gets content height when component mounts
// without setTimeout, measure returns 0 for every value.
// See https://github.com/facebook/react-native/issues/953
setTimeout(this._getContentHeight);
// tried setTimeout(this._getContentHeight.bind(this);
},
...
Now, when I debug the app in chrome, I see, that I never get to the _getContentHeight(). Now I am pretty sure that it has to do something with asynchronous calls etc. But is there a way to debug this, and see what values I get for height and content_height?
Help is needed, especially in understanding setTimeout/asynchronous function calls.

I'd suggest moving the call to measure inside componentDidMount to make sure your functionality works before abstracting it and dealing with any scoping or object reference issues that you might be facing now.
componentDidMount() {
this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => {
// Sets content height in state
this.setState({
height: this.props.expanded ? height : 0,
content_height: height
});
});
}
Update:
I've wrapped your code in a react class and got the measure object to error out # https://jsfiddle.net/x7w62w99/
var Hello = React.createClass({
_getContentHeight() {
console.log(this.refs)
if (this.refs.AccordionContent) {
this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => {
// Sets content height in state
this.setState({
height: this.props.expanded ? height : 0,
content_height: height
});
});
}
},
componentDidMount() {
setTimeout(this._getContentHeight);
},
render: function() {
return <div ref='AccordionContent'>
Hello {this.props.name}
<div id='AccordionContent'>
some stuff
</div>
</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);

Try this
setTimeout(function(){
this._getContentHeight.bind(this)
})

Use This:
setTimeout({
this._getContentHeight
}, the delay you want);
SetTimeout takes two parameteres.
http://www.w3schools.com/jsref/met_win_settimeout.asp

Related

Framer-motion drag not respecting previously updated props

A simple use-case is to allow a user to either click buttons to paginate in a slider, or drag. Both events call the same paginate function with a param to either go forward or back--simple stuff.
However, the trigger from drag seems to cause bizarre behavior where the slider wants to start the animation from several slides back as if it ignores the updated props. This doesn't happen when using the buttons and both use the same simple paginate call.
Any tips appreciated.
Minimal example:
export default function App() {
const [position, setPosition] = useState<number>(0);
const paginate = (direction: Direction) => {
setPosition((prev) => {
return direction === Direction.Forward
? Math.max(-800, prev - 200)
: Math.min(0, prev + 200);
});
};
return (
<div className="App">
<Slider>
<Wrapper
animate={{ x: position }}
transition={{
x: { duration: 1, type: "tween" }
}}
drag="x"
dragConstraints={{
top: 0,
left: 0,
right: 0,
bottom: 0
}}
onDragEnd={(e, { offset, velocity }) => {
const swipe = swipePower(offset.x, velocity.x);
if (swipe < -swipeConfidenceThreshold) {
paginate(Direction.Forward);
} else if (swipe > swipeConfidenceThreshold) {
paginate(Direction.Back);
}
}}
>
<Slide>1</Slide>
<Slide className="alt">2</Slide>
<Slide>3</Slide>
<Slide className="alt">4</Slide>
<Slide>5</Slide>
</Wrapper>
</Slider>
<button onClick={() => paginate(Direction.Back)}>prev</button>
<button onClick={() => paginate(Direction.Forward)}>next</button>
</div>
);
}
Codesandbox Demo
I have to say, this problem is quite interesting. However, I think I figured out a way for you to handle this. One thing I noticed is that if you comment out
onDragEnd={(e, { offset, velocity }) => {
// const swipe = swipePower(offset.x, velocity.x);
// if (swipe < -swipeConfidenceThreshold) {
// paginate(Direction.Forward);
// } else if (swipe > swipeConfidenceThreshold) {
// paginate(Direction.Back);
// }
}}
the entire onDragEnd prop function, this example still doesn't work, since by the looks of things, the draggable component is not respecting your offset.
I realized that at this point, the problem is the internal state of the component is out of sync with your state. And would you look at that, the Framer Motion API actually provides a way to inspect this.
https://www.framer.com/api/motion/motionvalue/#usemotionvalue
It's the hook useMotionValue() which allows us to see what's actually happening. Turns out, our value is being set wrong when the user starts dragging:
useEffect(
() =>
motionX.onChange((latest) => {
console.log("LATEST: ", latest);
}),
[]
);
We can see this, because the state "jumps" to 200 as soon as we start dragging.
So fixing in theory is easy, we just need to make sure to let that value "know" about our offset, and that way it's gonna start with the proper offset in mind!
Anyway, that was my thought process, here's the solution, all you need to do is set the left constraint to make it work:
dragConstraints={{
top: 0,
left: position,
right: 0,
bottom: 0
}}
And tada! This makes it work. Here's my working solution: https://codesandbox.io/s/lingering-waterfall-2tsfi?file=/src/App.tsx

Vuetify dynamic height for v-img

I have a container that is not 100% height of the page, it is 80% plus 60px on top and on bottom.
So the image inside should inherit the height of the container.
Using the <img /> and a little of CSS it is easy to achieve but I wish to add a template with loading spinner and so I started using the <v-img> for it.
I can see I can set the height or the max-height and so I did a method to calculate the parent height minus the 120px to get the exact image height, and it is working as expected.
The issue is when the user resize the window, I can see the method to be called and updating the value but the vuetify element is not responsive to the new values and so the image is bigger or smaller than container:
<div id="photoWrapper" class="photo-wrapper">
<v-img
:src="photo.url"
:contain="true"
:max-height="wrapperHeight()"
>
<template v-slot:placeholder>
<v-layout fill-height align-center justify-center ma-0>
<v-progress-circular
indeterminate
color="tertiary"
></v-progress-circular>
</v-layout>
</template>
</v-img>
</div>
Code:
data() {
return {
mounted: false
}
},
mounted() {
this.mounted = true
window.addEventListener('resize', this.wrapperHeight)
this.wrapperHeight()
},
methods: {
wrapperHeight() {
if (!this.mounted) return
console.log(document.getElementById('photoWrapper').offsetHeight - 120)
const height = document.getElementById('photoWrapper').offsetHeight - 120
return height
}
}
The method is always called on screen resize, but the image is not responsive.
How can I do it? I also tried to move the wrapperHeight to be a computed property.
You need to make your wrapperHeight is a computed property. That is correct and more performance than create it like method.
computed: {
wrapperHeight() {
if (!this.mounted) return
console.log(document.getElementById('photoWrapper').offsetHeight - 120)
const height = document.getElementById('photoWrapper').offsetHeight - 120
return height
}
}
And
:max-height="wrapperHeight" not :max-height="wrapperHeight()"
The solution is to do a mix between computed and methods like so:
data() {
return {
mounted: false,
containerHeight:
document.getElementById('photoWrapper').offsetHeight - 120
}
},
computed: {
wrapperHeight() {
if (!this.mounted) return
const height = this.containerHeight
return height
}
},
mounted() {
this.mounted = true
window.addEventListener('resize', _.debounce(this.handleResize, 100))
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
handleResize() {
this.containerHeight =
document.getElementById('photoWrapper').offsetHeight - 120
}
}
I think you have to return the ${height}px in the wrapperHeight method

Making text fade in/out on button click in React

I have a random quote generator written in React: https://codepen.io/1000mileworld/pen/RwNMwKE
When I click the new quote button, I would like the previous quote to fade out and the new quote to fade in.
I was trying to use some jQuery that changed the opacity to 0 before setting new state and then return opacity back to 1 afterwards. I probably have to do this outside handleClick() somewhere but not sure how.
handleClick() {
$("#text").animate({ opacity: 0 }, 500);
const quote = getRandomQuote();
this.setState({
text: quote.text,
author: quote.author
});
$("#text").animate({ opacity: 1 }, 500);
}
In general it's not recommended that you query the DOM (ie using selectors to pick an element to call $.animate() on) when rendering those elements with React.
Perhpas you could consider another approach that by-passes JQuery all together and instead uses CSS transitions and React to achieve the equvilant fade transition?
Something like this might work for you:
JSX
const FADE_DURATION = 1000;
class Application extends React.Component {
constructor(props){
super(props);
this.state = {
text: quote.text,
author: quote.author,
// Add extra state to control fading
fadeTransition : null,
fadeState : "fade-in",
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
// Create a timer that runs after quote
// fades out
const timeout = setTimeout(() => {
// After current quote faded out, fetch
// next quote and display
const quote = getRandomQuote();
this.setState({
text: quote.text,
author: quote.author,
// Apply additional state to control
// fading transition
fadeTransition : null,
fadeState : 'fade-in'
});
}, FADE_DURATION);
// Stop any existing transition
clearTimeout(this.state.fadeTransition);
// Update state to perform the fade out from
// current quote
this.setState({
fadeState : 'fade-out',
fadeTransition : timeout
});
}
render() {
return (
<div className={`fade-wrapper ${this.state.fadeState}`}
style={{ transitionDuration: `${FADE_DURATION}ms`}}>
<Display text={this.state.text}
author={this.state.author}
click={this.handleClick} />
</div>
);
}
}
CSS
/* Add this to existing css */
.fade-wrapper {
transition:opacity ease-in-out;
}
.fade-out {
opacity:0;
}
.fade-in {
opacity:1;
}
Here's a working example (sorry the CSS is a little broken) - hope that helps!
You can use the callback from animate, and set the state there. Something like:
$( "#txt" ).animate({
opacity: 0,
}, 2000, function() {
that.setState({text:"world"});
$( "#txt" ).animate({
opacity: 1,
}, 2000, function() {
// Animation complete.
});
You'll need to be aware that the scope of 'this' will need to be handled within the callback. I just did that=this in the example just to demonstrate the usage of the callback.
https://jsfiddle.net/pythagoras2357/8zarn94j/9/
https://api.jquery.com/animate/

Passing dynamic styles to my element in VueJS

I am trying to make a type of progress bar to track a percentage of tasks completed. I want to v-bind:styles and pass it {width: dynamicWidth + '%'} in order to control the progression of this bar. So far I have created a computed variable that will return the percentage complete I want to bar to display, and I have set up my dynamic style in the data object
export default{
data: function () {
return {
numQuotes: dataBus.numQuotes,
numberA: 30,
barWidth: {
width: this.barWidthCalculated +'%'
}
}
},
computed: {
barWidthCalculated: function(){
return this.numQuotes * 10;
}
}
}
I also added an element to the DOM to see what was happening.
<div id="trackerBar">
<div id="trackerBarActual" v-bind:style="barWidth">
<h2>{{numQuotes}}/10</h2>
<p>{{barWidthCalculated}}</p>
</div>
</div>
My bar stays fixed at 100%, i dont see any interpolation on the DOM. I also established another NUMBER variable in my data section and attempted to pass that to my width property, but still no change, and no rendering on the DOM. However if I pass any other elements in my styles object such as
color: 'red'
Those changes take place. Also if I pass my styles object a number directly ie...
barWidth: {
width: 50 +'%'
}
It displays correctly on the DOM.
What am I missing/doing wrong?
why not just use :
<div id="trackerBarActual" v-bind:style="barWidthCalculated">
computed: {
barWidthCalculated: function(){
return {
width: (this.numQuotes * 10) + '%',
color: 'red'
};
}

How to reset grid paging content on resolution change?

I have used a grid template from BootStrap and the paging for that grid is showing as per the attached image 1. Now when i change the screen resolution of the browser the paging content gets break into two lines as per attached image 2 instead of in a single line.
But once i refresh the page it loads the paging correctly as the image 3. But i want that content right after changing of resolution.
Below is the code of JS by using React for that paging class
var ul = React.DOM.ul({className: 'pagination pagination-sm', role: 'menubar', 'aria-label': globalizationFactory.translate('STORMS_PAGER'), key: 'pager_container'}, [firstLi, prevLi, this.getPages(currentPageIndex + 1, totalPages), nextLi, lastLi]);
var mainClass = this.props.className ? 'container-fluid ' + this.props.className : 'container-fluid';
return React.DOM.div({className: mainClass,key: 'pager_wrapper'},
React.DOM.div({className: 'row',key: 'pager_wrapper_inner'},
React.DOM.div({className: 'col-sm-12', style: {textAlign: 'center'},key: 'pager_innerWrapper'}, ul)));
Any suggestion will be helpful.
Image 1:
Image 2:
Image 3:
I think all you need to do is listen for window resize events and have that trigger a render. In React, if you change state, your component will render. You can do something like this inside of your component that is doing the rendering.
getInitialState: function() {
return {
windowWidth: window.innerWidth,
windowHeight: window.innerHeight
};
},
componentDidMount: function() {
return window.addEventListener('resize', this._handleResize);
},
componentWillUnmount: function() {
return window.removeEventListener('resize', this._handleResize);
},
_handleResize: function() {
if (this.state.windowWidth !== window.innerWidth || this.state.windowHeight !== window.innerHeight) {
return this.setState({
windowWidth: window.innerWidth,
windowHeight: window.innerHeight
});
}
}

Categories