React native ScrollView resets positon on setState - javascript

I have a ScrollView with buttons on it. I populate ScrollView dynamicaly. After I press on the buttons inside the ScrollView, I'm changing the state of the current selected index and the posiotion of ScrollView is resetting.
<ScrollView
ref={ref => {
this._scrollview = ref;
}}
style={styles.scrollview}
horizontal
showsHorizontalScrollIndicator={false}
snapToInterval={64}
snapToAlignment="start"
decelerationRate="fast"
contentContainerStyle={{ paddingRight: 8, paddingLeft: 8 }}>
{this._channels.map((item, index) =>
<TouchableWithoutFeedback
onLayout={event => {
const layout = event.nativeEvent.layout;
this._channelsPositions[index] = layout.x;
}}
key={item.userId}
onPress={() => this._changeIndex(index)}>
<Image
source={{ uri: videos[index][0].channelThumbnails.high.url }}
style={[styles.channelLogo, index == this.state.currentIndex ? { borderWidth: 2 } : {}]}
/>
</TouchableWithoutFeedback>
)}
</ScrollView>
_changeIndex = (newIndex: number) => {
const { currentIndex } = this.state;
if (newIndex == currentIndex) return;
this.setState({
currentIndex: newIndex
});
const newX = 16 + 48 * newIndex
this._scrollview.scrollTo({
x: 16 + 48 * newIndex,
y: 0,
animated: true
})
}
And after I press the button I want ScrollView to move to x position of this button inside the ScrollView.
Demo: https://streamable.com/gu1edy
Update: After i replace scrollview for Flatlist in header i think i understand why is it happening. But i still don't know how to fix it. So the behavior is same, i am getting scroll reset every time i press on the button/cell/item of scrollview/flatlist.
How looks my flatlist with nested flatlist in header
\ |CHANNEL ICON 0|CHANNEL ICON 1|CHANNEL ICON 2| ... \ header with flatlist
\ FIRST ITEM IN FLATLIST WHICH CONTAINS VIDEO OF CHANNEL\ cell 0
\ SECOND ITEM IN FLATLIST WHICH CONTAINS VIDEO OF CHANNEL\ cell 1
...
So after i klick on channel icon all main flatlist populates with new data so if i understand right the header rerendering and repopulates with new data too. So is there a possibility to update header with flatlist that way my scroll in header stays on it's position without resetting?

Hi I have been fixed it by moving Scrollview from my partial Functional Component to main Function Component screen.
For example you have two functions :
component RenderListProducts
screen/page Product
So the solution is move Scrollview of Flatlist inside RenderListProducts and paste it to indside of screen/page Product.
Try it. If you still have this problem, please put comment below.

Related

Error in styling when using function to determine flex React Native

Im trying to create 2 dropdown menus where depending on which are clicked would reduce a third view's flex number to accommodate the other 2 expanded views. Im using two useState variables to keep track of the 2 different dropdown menus, and calculate the flex as shown:
const [isFirstExpanded, setIsFirstExpanded] = useState(false)
const [isSecondExpanded, setIsSecondExpanded] = useState(false)
const calculateFlex = () => {
if (isFirstExpanded && isSecondExpanded) {
return 0; // This means both dropdowns are expanded, and should hide the third view
} else if (isFirstExpanded || isSecondExpanded) {
return 2; // Only one of the dropdowns are open, so half the size of the third view
} else {
return 4; // None of the dropdowns are open, so show the full view
}
}
<View style={{flex: 1}}>
<View style={{flex: calculateFlex, backgroundColor: "red"}}/> // View to be grown or shrunk depending on below views
<TouchableOpacity style={{flex: 1}} onPress={setIsFirstExpanded(!isFirstExpanded)}/>
<View style={isFirstExpanded ? styles.expandView : styles.hideView}/> // The dropdown that is hidden by default
<TouchableOpacity style={{flex: 1}} onPress={setIsSecondExpanded(!isSecondExpanded)}/>
<View style={isSecondExpanded ? styles.expandView : styles.hideView}/> // The dropdown that is hidden by default
</View>
This code leaves out some innocuous things, if needed I can copy/paste them. My problem occurs when the first view to be grown or shrunk doesn't seem to follow the calculate flex function. Not sure why, or if there is a better way to calculate this.

React Native - Disable nested scrolling

I have a parent FlatList which, in its footer, can render another flatlist or a simple scroll view.
const renderFooter1 = () => {
return <ScrollView> ... </ScrollView>;
}
const renderFooter2 = () => {
return <FlatList ... />;
}
// Note: I am not doing this in real life, just an example for handling both possible footers
return (
<FlatList
refreshControl={renderRefreshControl()}
ListHeaderComponent={renderHeader()}
ListFooterComponent={Math.random() * 10 > 0.5 ? renderFooter1() : renderFooter2()}
showsVerticalScrollIndicator={false}
/>
);
The problem I am experiencing is that, sometimes, when scrolling down, the component which scrolls is the footer, and not the parent list.
How can I avoid that behavior?
You can add the prop scrollEnabled={false} to whichever FlatList you don't want to scroll.

Material UI React Slider Component Not Working on mobile

I have been trying to add Slider component to a react project. functionality wise its working fine but i am having two issues which i am not able to get rid of
changing value of the slider is not smooth. Dragging doesn't work properly, its just drags to the nearest value and then stops.
On a mobile device its even worse, no dragging at all, i have to tap on the exact spot for the slider to move.
I did find the issue, i was using onChange, so when i removed it it worked exactly like the example. But i need to update state of parent component, so added line 18, but then again the same issue appeared. I fi remove line 18 then all this gets fixed but i need line 18 to call a function of parent component, to update its state variable.
Here is the gist link of my code
https://gist.github.com/kapiljhajhria/0e9beda641d561ef4448abf9195dbcca
import React from "react";
import Slider from "#material-ui/core/Slider";
export default function SliderWithLabel(props) {
const {
labelText, range = {
min: 0,
max: 10
}, step = 1,
// defaultValue = Math.ceil((range.min + range.max) / 2),
handleSliderChange,
name,
value: sliderValue
} = props;
function sliderValuetext(value) {
// handleChange({target: {value: value}});
if(value!==sliderValue)handleSliderChange(value,name)
return `${value}`;
}
return (
<div className="sliderField" style={{display: "flex", flexDirection: "column"}}>
<div>
{labelText}
</div>
<Slider
style={{width: "90%", justifyContent: "center", display: "flex", margin: "auto"}}
defaultValue={sliderValue}
getAriaValueText={sliderValuetext}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
// onChange={sliderChange}
step={step}
// name={name}
// onChange={handleChange}
marks
min={range.min}
max={range.max}
/>
</div>
)
}
after spending 2 days on the issue, creating a sample project , trying to recreate the issue , it turned out to be a simple fix.
Parent component has a FORM, key which i was using for the form was
Date().getTime()
This was what was causing the issue with the slider. My guess would be that it was rebuilding the whole form with each slider value change. Which made slider UI behave in such a way. using appropraite key fixed the issue. I am now switching between two key value.

Text editor value disappears in React Js

I am making a simple accordion and inside each accordion, there is a text editor.
Accordion.js
<div className="wrapper">
{accordionData.map((item, index) => (
<Accordion>
<Heading>
<div
style={{ padding: "10px", cursor: "pointer" }}
className="heading"
onClick={() => toggleHandler(index)}
>
{toggleValue !== index ? `Expand` : `Shrink`}
</div>
</Heading>
<Text> {toggleValue === index && item.content && <EditorContainer />} </Text>
</Accordion>
))}
</div>
Here accordion is made up of as a component. This line {toggleValue === index && item.content && <EditorContainer />} is made to check the accordion clicked and then it loads the content and text editor accordingly.
Complete working example:
https://codesandbox.io/s/react-accordion-forked-dcqbo
Steps to reproduce the issue:
-> Open the above link
-> There will be three accordion
-> Click on any of the accordion, that will change the text from Expand to Shrink
-> Now fill some random text inside the editor then click on the text Shrink
-> Again open the same accordion by clicking Expand
-> Now already entered value is missing
I doubt it happens because every time we expand/shrink, the text_editor.js component gets called and that has the state value like,
this.state = {
editorState: EditorState.createEmpty()
};
Here instead of EditorState.createEmpty(), Should I need to give any other thing?
Requirement:
How can I store the already entered value in the text editor. Even though user clicks expand/shrink, the entered text needs to be remain there in the editor.
Any help is much appreciated.
You are correct, the entered value is missing because you are unmounting the EditorContainer component when its shrinked — that when you expand it again it creates a new editorState which is empty.
2 Possible solutions I could think of.
Move editorState and onEditorStateChange to the Parent component and pass that to EditorContainer. This way, when we unmount the EditorContainer we won't lose the previous editorState because it's on the Parent.
We wrap our EditorContainer inside a div and we'll apply a display style when we toggle between shrink/expand. This way, we are only hiding the EditorContainer not unmounting so its states will retain.
I would choose to implement the 2nd solution because we only have to make changes to our Accordion.js file. In either ways, I would create a new component that would handle the current item. I call it NormalAccordionItem.
const NormalAccordionItem = ({ data }) => {
const [show, setShow] = useState(false);
function toggle() {
setShow((prev) => !prev);
}
return (
<Accordion>
<Heading>
<div
style={{ padding: "10px", cursor: "pointer" }}
className="heading"
onClick={toggle}
>
{show ? "Shrink" : "Expand"}
</div>
</Heading>
<Text>
<div style={{ display: show ? "block" : "none" }}> // notice this
<EditorContainer />
</div>
</Text>
</Accordion>
);
};
Then on our NormalAccordion we'll use NormalAccordionItem.
const NormalAccordion = () => {
return (
<div className="wrapper">
{accordionData.map((data) => (
<NormalAccordionItem data={data} key={data.id} />
))}
</div>
);
};
That's it, check the demo below.
Edit Updated demo to expand NormalAccordionItem one at a time.

React resizable only resizing the width of the box, not the height

I have a React-mui draggable dialog component on which I am using resizable box:
return (
<StyledDialog
open={open}
classes={{root: classes.dialog, paper: classes.paper}}
PaperComponent={PaperComponent}
aria-labelledby="draggable-dialog"
>
<ResizableBox
height={520}
width={370}
minConstraints={[300, 500]}
maxConstraints={[Infinity, Infinity]}
className={classes.resizable}
>
<DialogContent classes={{root: classes.dialogContent}} id="draggable-dialog">
<IconButton className={classes.clearIcon} aria-label="Clear" onClick={onClose}>
<ClearIcon/>
</IconButton>
<iframe
src={hjelpemiddel.url}
title={hjelpemiddel.navn}
width="100%"
height="100%">
</iframe>
</DialogContent>
</ResizableBox>
</StyledDialog>
);
I would like to resize the iframe inside the dialog along with the ResizableBox. But, it seems I can only resize the width of the ResizableBox, and not the height of the box, at least the maximum height seems to be the one that is set initially. How can I fix that, so that I can resize the height as well?
UPDATE
Codesanbox is available here.
FYI, for some reason sometimes import fail message appears, but everything works fine if you refresh the page of the codesandbox.
In your CodeSandBox, based on my testing, the events for dragging & resizing are simultaneously firing. You could use the cancel prop of react-draggable so that the dragging would not occur when the resize handle is the component being interacted with.
<Draggable
cancel={".react-resizable-handle"}
...
When you do this, the draggable element will not be updating its CSS transform: translate property anymore while resizing - for this, you can opt to control your Draggable component's position. Translate/Set X & Y as necessary to retain its position while resizing. Note that the x & y state & state setters should be residing on a common parent/ancestor among these components that you will be passing down as props.
export default function App() {
// have the source of truth for the positions on a common parent/ancestor
const [x, setX] = React.useState(0);
const [y, setY] = React.useState(0);
return (
<div className="App">
<PDFDialog x={x} y={y} setX={setX} setY={setY} />
</div>
);
}
...
class PDFDialog extends React.Component {
state = {
open: true
};
render() {
const { open } = this.state;
const { classes } = this.props;
return (
<StyledDialog
open={open}
classes={{ root: classes.dialog, paper: classes.paper }}
PaperComponent={PaperComponent}
aria-labelledby="draggable-dialog"
PaperProps={{
x: this.props.x,
y: this.props.y,
setX: this.props.setX,
setY: this.props.setY
}}
>
<ResizableBox
height={520}
width={370}
minConstraints={[300, 500]}
maxConstraints={[Infinity, Infinity]}
className={classes.resizable}
onResize={(e) => {
if (e.movementX !== 0) {
this.props.setX((prev) => prev + e.movementX);
} else if (e.movementY !== 0) {
this.props.setY((prev) => prev + e.movementY / 2);
}
}}
></ResizableBox>
...
// refactored draggable component:
<Draggable
position={{ x: x, y: y }}
cancel={".react-resizable-handle"} // <-- cancel the dragging if resize is interacted with
onDrag={(e) => {
if (e.movementX !== 0) {
setX((prev) => prev + e.movementX);
}
if (e.movementY !== 0) {
setY((prev) => prev + e.movementY);
}
}}
>
<Paper {...props} />
</Draggable>
(On my CodeSandBox, I've gotten rid of constraints such as minimum height & width to clearly show the example)
You can bind the event of resize of window, calculate the new height and width and pass it to the resizable-box of yours
object.addEventListener("resize", function() {
//Here you can write logic to apply resizing on the resizable-box
});
Obviously, react-resizable uses inline CSS to handle width and height of a box, and for your issue, I simulate this issue, pay attention to the below screenshot from Google chrome devTools:
The react-resizable-box has an important flag and it overwrites the inline height value so in the view, I have the following behavior:
Your information is not enough so I cannot say directly your issue cause or causes but it is very probable CSS overwriting is the root cause of this issue.
So, inspect the resizable-box on your project and seek to find CSS overwriting.
Update after adding re-produce the issue
Actually, based on my last answer something overwrite your height now in the re-production sandbox, I delete the iframe tag, and everything works well, you pass a height="100%" attribute to your iframe tag and it prevents the change of height. Also, you pass a minConstraints={[300, 500]} to your ResizableBox component, so it could not have a smaller height than 500px.

Categories