Nested component state control - react-native

I have an overlay/modal within a screen that resides under a parent container that I would like to control as follows:
On load, visibility is false
When user performs and act on the screen/container component, the overlay shows.
The user can close the overlay by clicking on the area outside the overlay (onBackdropPress)
Data within the overlay is provided by the parent container (through state).
Should I generate a state within the screen component and assign props to it?
class Screen extends Component {
state = { visible: this.props.visible, data: this.props.data }
render () {
return (<Overlay isVisible={this.state.visible} onBackdropPress={this.setState({visible: false})}><Text>{data}</Text></Overlay>)
}
}
How do I deal with the problem that by setting the state within the Screen component, the parent container's state is not updated yet?
Visibility is still set to true in the parent container even though it's already set to false in the screen component.
<Screen visible={this.state.visible} data={this.state.data}>

Related

Close v-navigation-drawer from parent component when clicking outside

I have a navigation drawer that is its own component. The toggling of visibility is controlled from the parent component by passing a prop that is assigned as the v-model of the v-navigation-drawer. I then emit an event form child to update the prop from the parent. That works fine. However, when clicking from outside, the navigation drawer doesn't close. I came up with the solution below to manually close the navigation drawer on outside click. I added a condition so that it only responds when the target is the overlay. Is there a better way of handling this so that the navigation-drawer closes when clicking only on the overlay and not on other elements from the parent?
//DrawerComponent.vue
<v-navigation-drawer
v-model="isDrawerShowing"
temporary
fixed
:width=width
:height="height"
right
stateless
v-click-outside="hideDrawer"
></v-navigation-drawer>
hideDrawer(e) {
if(e.target.className === "v-overlay__scrim") {
this.$emit('hide-drawer',false);
}
}

how can I clean route parameters previously passed from child screen in my case

I am developing a react-native project. Here is my main screen which can receive route parameters from child screen.
(User can navigate from MainScreen to ChildScreen and vise versa.)
const MainScreen = ({route, navigation}) => {
if (route?.params) {
//do something
} else {
//do something else
}
...
}
When navigating back from child screen to main screen, inside ChildScreen, I have:
//this is inside Child screen
onPress={() =>
navigation.navigate("MainScreen", {data: 'foo'})
}
Above code snippet is a illustration of how I pass route parameter from child screen to main screen.
Overall, it works well. However there is one issue. That's inside the MainScreen, once navigating back from ChildScreen to MainScreen, the value of the parameter inside route?.params is preserved. I mean if I kill the app's process and launch the app again which shows MainScreen again, the
if (route?.params) {
//do something
}
is executed because the route?.params contains the previously passed parameters from child screen. That is bad since I expect a clean start for my main screen. How to get rid of this issue?
Try navigation.push instead of navigation.navigate.

What is the best way to initialize a state in ReactNative with custom values on a Modal?

So, I have a specific problem where i need to initialize a state with custom values that comes from the parent component props.theValue. But the problem is that the props.theValue isn't ready when we set the state. So this line should never work:
const [grams, setGrams] = useState(props.theValue)
The scenario:
We have a list of food on the Parent Screen, when the users Clicks on it, it opens a modal (Like a PopUp) with some options to choose before the selected item gets manipulated
What I tried?
Set initial flag useState(-1) to indicate it should set the value from props but again, the props isn't ready in this state
Save the state on the parent and pass the state with the set Function to the modal. But still it doesn't seems to be right
useEffect but whenever i call setGrams it get a Loop Rendering error, also the props.theValue is still undefined at this point
Example:
In this case, the both TextInput must have a initial value that comes
from the Parent Component and must be manipulated.
You could use Redux for manipulating your data between Components or you can try something like:
set a Global variable:
let previousObj = null
and then set a timeout to update your data only once, even if you set Modal invisible and Visible again
setTimeout(() => {
if(food.grams != undefined && previousObj != food){
previousObj = food
setGrams(""+food.grams)
}
}, 100)
You can either handle it in the child with default value or don't render the component in parent until the value is available:
{theValue && <Component theValue={theValue} />}
You can use useEffect to update the default value as well:
const [grams, setGrams] = useState(0)
useEffect(() => {
if (theValue) {
setGrams(theValue);
}
}, [theValue])

How to re-render part of the component upon state change?

I have a component the includes progress.circle from the library react-native-progress (see link: https://github.com/oblador/react-native-progress).
Initially I set the progress prop of the progress circle to a state variable that is set to 0 at idle state as shown in the code below:
<Progress.Circle
showsText={true}
progress={this.state.progress} // this.state.progress = 0
size={width * 0.1388888888888}
color={"#EF5D6C"}
/>
Number of user interactions take place to set the state variable (this.state.progress) to a certain percentage then the component re-render to show the progress animation as intended.
Problem is the component flashes for part of a second as result of the re-rendering process.
So is there a way to only re-render the progress circle and leave the rest of the component in its current rendering form.
You could try separating out the Progress.Circle element into a different file so its in its own renderer.
render() {
return (
<Progress.Circle
showsText={true}
progress={this.state.progress} // this.state.progress = 0
size={width * 0.1388888888888}
color={"#EF5D6C"}
/>
)
}
That way when you change the element in state only the progress.circle should re-render. Then you would import this into your main file and call it there:
render(){
....
<ProgressCircle />
....
}
Basically whatever is inside the render function will re-render. If the progress.circle element is on its own where the state is changed, only it should re-render as far as i know.
Hope this helps in some way!
Make the progress.circle view as a separate component, as suggested in the previous answer. Keep the progress value in global state, which can be accessed by this separate component. If you are using Redux, you can do this easily. Then pass that state.progress value in the progress prop of the Progress.Circle

react native component inside component

I have component that includes a textInput and another component with a slider that also has a textInput. Since both textInputs are exactly the same I am importing the component with the textInput into the component with the slider. When I focus on the textInput I want to make the slider disappear (focus: true -> hide slider , focus: false -> show slider)
Since the textInput is on a child component I'm using a callback to get the focus state of the child and based on this update the focus state on the parent.
The issue is that since I'm updating the state of the parent the whole thing gets rerendered and this also rerenders the child which returns the state again which makes the parent rerender again and makes the child rerender again which makes the child return the state and rerender the parent again and so on.
I did a console.log on both components and it seems this happens around 5 times until it stops. Sometimes with the slider showing even though focus is true other times the slider is hidden.
My question will be how do I make this run only once or at least how do I make the slider always hidden when focus = true (I have merge everything in one component and that solves the issue but I have another view that only needs the textInput so it will be great if I can split this in two components)
I think you're trying to change the state of the parent component using a lifecycle method like componentDidUpdate and that causes the rendering loop. Instead, use a function as a prop to set the new state, then use shouldComponentUpdate method like this:
In the child component:
func(param) {
this.props.setParam(param)
}
And in the parent component:
setParam(param) {
this.setState(
// set the param here
);
}
shouldComponentUpdate(nextProps, nextState) {
// do something with nextState
}
render() {
// do something
return <Child setParam={this.setParam} ... >;
}
That should work.