Changing state with body width change in React - javascript

I have to change state when I'm changing width of the document. How can I do it in React? Any suggestions how to get body element and using onChange on it?

componentDidMount() {
window.onresize = () => this.setState({ width: window.innerWidth });
}

You need first return 2 different view based on the screen size, you can get help from here:
http://www.alistapart.com/articles/responsive-web-design/
then every time the screen changes (based on values you use on each view), componentDidMount will be called and you can change the state there.

Related

Inconsistent useRef().clientHeight

I have a parent div card which can display one of three child divs. I want to adjust the height of the parent div to fit the exact contents of the currently-visible child div.
To do this, I create a updateDonationCardSize function which reads the refs of all three views, and depending on which one should be currently visible, sets the parent div height to match the appropriate child div height.
function updateDonationCardSize() { // Sets the size of the card to fit contents
const height = stepOneContainerRef.current.clientHeight
setDonationCardSize(prevState => ({
...prevState,
['width']: paymentFooterRef.current.clientWidth,
['height']: height
}))
}
I call this function whenever any of the refs pointing to one of the subviews updates.
useEffect(() => {
updateDonationCardSize()
setTimeout(() => updateDonationCardSize(), 700)
}, [stepOneContainerRef, stepTwoContainerRef, stepThreeContainerRef])
Notice the silly approach above where the function is called twice, once immediately and once after a brief delay. For some reason, the first time it runs, it returns the wrong height of 830 whereas the second time it runs, it returns the correct height of 700.
What's even stranger is that if I print out height variable as well as the stepOneContainerRef object into the console next to each other, the height variable is returned as 830 while stepOneContainerRef object actually displays its current.clientWidth property as 700...
I am completely clueless and have spent hours over this already.
Edit - Providing additional information
The parent div contains the following structure:
<div id='parent div' className='overflow-hidden'>
<div id='parent of dynamic content'>
<div id='containerOne' className='relative' style={stepOneContainerStyle} />
<div id='containerTwo' className='relative' style={stepTwoContainerStyle} />
<div id='containerThree' className='relative' style={stepThreeContainerStyle} />
</div>
<div id='footer' />
</div>
and the stepXContainerStyle is a dictionary which sets position: top and x to 0 if it the particular div should be currently visible, otherwise it moves it outside of the overflow region to hide it.
The different heights are result of unfinished DOM mutations during the rendering of the components. To fix that change useEffect hook to useLayoutEffect which fires synchronously after all DOM mutations. It will allow you to call updateDonationCardSize only once.
In addition, you should wrap updateDonationCardSize with useCallback for correct hook dependency specification.
const updateDonationCardSize = useCallback(() => { // Sets the size of the card to fit contents
setDonationCardSize(prevState => ({
...prevState,
['width']: paymentFooterRef.current.clientWidth,
['height']: stepOneContainerRef.current.clientHeight
}))
}, [paymentFooterRef, stepOneContainerRef]);
useLayoutEffect(() => {
updateDonationCardSize()
}, [updateDonationCardSize])
I'd also recommand to try solve this problem with CSS rules - it looks like a CSS problem more than component rendering problem.

The background image style is not applied with setInterval in React.js

Using AJAX request, I am trying to change background image and set it's style properties. In the function which is called every few seconds through setInterval method, I define style and set states of component in the following way:
changeThings() {
let maxVal = this.props.data.length;
let ranNum = Math.floor((Math.random() * maxVal));
let imgVal = (this.props.url[ranNum])? 'url('+ this.props.url[ranNum].url+')':null;
let style = {
background:imgVal,
backgroundSize:'cover',
backgroundPosition:'center',
bacgkrouhndRepeat:'no-repeate'
};
this.setState({content:{style:style,
section:section,
title:title,
by:author,
dateInfo:updatedDate}});
}
render() {
return ({
<div>
//...other components
(this.state.content.style)?
<div id="image" style={this.state.content.style}>:null
//...other components
</div>
})
}
The very first image is displayed with every background image css properties applied. From second images however, it only changes images(background:url value) but not other background image properties such as position, repeat, size etc.
What is the reason of this problems and How can I solve it?
Here is how I fixed the problem. First I only saved URL string data to this.state.content rather than entire css style. It worked when I set the style property directly to the JSX.
Also, basically when DOM define the value for style property of background-size, it calculates the value based on the current 'snapshot' value of element height and width where the image will be display. I am currently making responsive web app so I thought the css style does not work when I changed the view.

gridstack.js set grid-stack-item height auto based on inner content

Is there any option to use gridstack.js with each grid-stack-item height based on inner content like image.
In my scenario each images having different height so it's not showing correctly on card.
It gives scrollbar inside grid-stack-item to view image, that is wrong.
Any option or workaround or other JS plugin/library that will restrict fixed height based on any factor.
Current behavior
Expected behavior
Height will be 100% based on width so image aspect ratio preserves.
I managed to do by setting up cellHeight : 10 then get image in resizestop event and update updateWidget.
Below is sample code for that resizestop event.
$('.grid-stack').on('resizestop', function (e, item) {
let stackItem = me.findGridStackItem(item.element.find('img').data('Id'));
if (stackItem) {
setTimeout(() => {
stackItem.option.height = Math.ceil(($(stackItem.nativeElement).find('img').height() + 5) / 10);
me.gridStackMain.updateWidget(stackItem);
}, 200);
}
});
the problem you are facing is about creating "masonry layout". I think gridstack is not the tool you need to use to resolve this problem. Try https://masonry.desandro.com/ or any other masonry layout lib.

JQuery function in ReactJS for a dynamic viewport height

so I got some help in another question on making a table's height equal to the viewport's height. Meaning, if the user resizes the screen, the table height adjusts on-the-fly to occupy the entire height of the screen. My problem now is, this is a React App and I am having a hard time converting this jquery function to React.
My function looks like this:
$(document).ready(function() {
function setHeight() {
windowHeight = $(window).innerHeight();
$('.dynamicHeight').css('height', windowHeight + 'px');
};
setHeight();
$(window).resize(function() {
setHeight();
});
});
Here is a codepen showing the behavior
And here is a screen shot of what I am trying to do
How can I build this function in React? I'm assuming it will need some modification.
Thanks in advance
NOTE: this may look like another question I made, but it is not a duplicate. It's an entirely different question related to the same issue.
In the components lifecycle, you should add a componentDidMount method. In that method, simply add a resize handler
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props){
super(props);
this.state = {
width: $(window).width(),
height: $(window).height(),
}
this.resize = this.resize.bind(this);
}
resize(){
this.setState(() => {
return {
width: $(window).width(),
height: $(window).height()
}
});
}
componentDidMount(){
window.addEventListener('resize', this.resize);
}
render(){
return (
<div>
<MyComponent width={this.state.width} height={this.state.height} />
</div>
)
}
}
So what this does is, when your Component initializes, you set the state to the width and height of the viewport (using jQuery). Then you define a method to update the state, and when your component mounts you attach an resize event listener to it and call the resize method each time the screen resizes. This ensures that your state always contains the width and height of the viewport. Then you can define the width and height of your component by passing that state to your component as props and boom! All good, everytime you resize your screen your component will match it.
If you have any questions let me know. You might need to tinker with this to get it to work (specifically the lifecycle events might not update state as often as you need to) I just whipped it up and did not test it but in theory this is exactly what you need
edit: just a thought, if you want to change the style of your components I would use a style object. So in the child component, do something like this:
let style = {
width: this.props.width + 'px',
height: this.props.height + 'px'
}
And when you render that component
<ChildComponent style={style} />

ExtJS: How to get the width of a button before it's added?

Is there a way to get the width of button before its added to the panel in a situation such as follows:
var myButton = Ext.widget('button', {
text : 'Hello'
});
myButton.getWidth(); //This is undefined
panel.add(myButton);
I know that since the button is't rendered on screen and I haven't explicitly given it a width, there is no way to know the width at this time. But is there an event or something else just before the button is rendered that will let me know its width before it's displayed on screen ?
Simply you can't, if you have a look to the button source code his dimensions are setted on his render.
You shouldn't work with the button width, instead you should use other layouts.
Probably there is a way, but it seems crazy:
var myButton = Ext.widget('button', {
text : 'Hello'
}),
clone=Ext.clone(myButton);
panel.add(clone);
clone.getWidth();//This is width
clone.destroy();
panel.add(myButton);
Like you can see the problem is on creating and adding it to only get his width.
It has probably no sense, but if you need to do something else change the question with something more clear. And surely we can get an answer to your problem.

Categories

Resources