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.
Related
Hello guys I just started learning React-Native and I have a question about state.
I was practicing this concept trying to make a button that shows how many times I've pressed it.
My plan was to make a variable called clicks which will increase by 1 each time I press it and set the clickState to clicks. This is my code.
export default function App() {
const [clickState, setClicks] = useState(0)
let clicks = 0
return (
<View style = {styles.container}>
<StatusBar style="auto" />
<TouchableOpacity style={styles.button} onPress={()=>{setClicks(++clicks); console.log(clicks)}}>
<Text>Clicks : {clickState}</Text>
</TouchableOpacity>
</View>
);
}
this is the console
But apparently something is wrong and my clicks value goes random between 1 and 2 each time I click it instead of increasing by 1.
So I was curious about what I was doing wrong and why the values don't increase as I expected. I would also be glad if you showed how you would implement it if there is a better way.
Thanks guys.
You only need to update clickState, no need of variable clicks.
Also it won't rerender if we increment state value directly, so we should increment state by taking its previous state value like shown below
export default function App() {
const [clickState, setClicks] = useState(0)
return (
<View style = {styles.container}>
<StatusBar style="auto" />
<TouchableOpacity style={styles.button} onPress={()=>setClicks(prevState => prevState + 1)}>
<Text>Clicks : {clickState}</Text>
</TouchableOpacity>
</View>
);
}
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.
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.
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.
Problem
I am using a flatlist in react native, and want to compare to variables in the flatlist, and render a text component if the two variables are equal, but render nothing if they are not. I've tried many methods of doing this, but they nothing has worked. I would love some help figuring out a way to do this! Thank you.
Couple ways that spring to mind straight away. Ill just assume what you are trying to compare but you can switch these variables out for whatever you please. First thing you could do is have your text be conditional inside of your Text component, EG
<Text>{this.state.variable == this.props.stuff ? "RENDER TEXT" : ""}</Text>
or, if you want to emit the Text component when variables are not equal, have a function inside of your class that will return the Text component conditionally
renderMessage = () => {
if(this.state.variable == "stuff"){
return(
<Text>They were equal</Text>
);
}else{
return(
<View></View> // OR WHATEVER YOU WANT HERE
);
}
}
...
//Then in your render function
....
<View style = {styles.whatever}>
{this.renderMessage()}
</View>
just compare your data in renderItem method accordingly
<FlatList
data={this.props.data}
renderItem={this._renderItem}
/>
_renderItem = ({item}) => (
if(item=='somthing'){
return <Text> your test </Text>
}else{
return <Text>some other text</Text>
}
);
if you want to compare your text in component then
<View>
{
item.data == 'somthing'
?
<Text>if</Text>
:
<Text>else</Text>
}
</View>