How to control of open/close modal in react-native - javascript

I want to control of 'Opening and Closing Modals' without using Redux. If I use Redux, i create a variable as isVisible and control it with setting a value as true or false. But, If I dont use Redux, i cant solve problem that explained below.
It's the code that I call modal function.
<TouchableOpacity onPress={ () => this.setState({ visibleModal: true ])}>
<Text> Press me! </Text> </TouchableOpacity>
<ProfileModals isVisible={this.state.visibleModal} />
Main Modal function:
export default class ProfileModals extends Component {
state = {
visibleModal: this.props.isVisible,
modal: "changeName"
};
render() {
const {visibleModal} = this.state;
return (
<Modal
isVisible={this.state.visibleModal}
backdropColor="black"
backdropOpacity={0.5}
animationOut="fadeOut"
onBackdropPress={() => this.setState({ visibleModal: false })}
swipeDirection={"down"}
onSwipeComplete={() => this.setState({ visibleModal: false })}
>
<View>
<Text>Hello!</Text>
</View>
</Modal>
);
}
}
When I press the button at the first time, Modal run correctly. After closing the modal, I press the button second time, It doesn't run anymore. Because of this.state.visibleModal value (in ProfileModal Component) is false, not this.props.isVisible.
How can i solve this problem?

dont store the modal's visibility in the state of the modal component, allow it to flow through props from the parent, follow this approach:
class Parent extends Component {
state = {
modalVisible: false,
};
openModal = () => this.setState({ modalVisible: true });
closeModal = () => this.setState({ modalVisible: false });
render() {
const { modalVisible } = this.state;
return (
<OtherStuff>
<Button onPress={this.openModal} />
<MyModal
visible={modalVisible}
closeModal={this.closeModal}
/>
</OtherStuff>
);
}
}
class MyModal extends Component {
render() {
const { visible, closeModal } = this.props;
return (
<Modal isVisible={visible}>
<Button onPress={closeModal} />
</Modal>
);
}
}

Related

function in child component can't setState in parent component

I am using react for the project. Here is my code for parent component:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
deleteConfirm: false
};
}
onDelete = pass => {
this.setState({ deleteConfirm: false });
if (pass === true) {
const { menu, onDelete, restaurantId } = this.props;
//onDelete(menu.id, restaurantId);
}
};
openDiag() {
this.setState({ deleteConfirm: true });
}
render() {
return (
<div>
<Button
className={classes.button}
variant="contained"
color="secondary"
onClick={this.openDiag.bind(this)}
>
Delete
{this.state.deleteConfirm ? (
<Child onDelete={this.onDelete.bind(this)} />
) : null}
<Delete className={classes.rightIcon} />
</Button>
</div>
);
}
}
The parent component has state called deleteConfirm, If I press Delete button, deleteConfirm is set to true and the child component is rendered. The function OnDelete passes to child component,
the child component is a Material Ui Dialog which is used to confirm the delete action. Here is the code:
class Child extends Component {
state = {
open: true
};
handleClose = (e, confirm) => {
const { onDelete } = this.props;
if (confirm === true) {
onDelete(true);
} else {
onDelete(false);
}
this.setState({ open: false });
};
render() {
return (
<div>
<Dialog
open={this.state.open}
onClose={e => this.handleClose(e, false)}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogActions>
<Button onClick={e => this.handleClose(e, false)}
color="primary">
Cancel
</Button>
<Button
onClick={e => this.handleClose(e, true)}
color="primary"
autoFocus
>
Confirm
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
If I press the cancel button, the delete action is cancelled and the parent function onDelete is called. the deleteConfirm in parent component should be set to false. However, the state is always true and I can't change it. I am greatly appreciated for anybody who would like to help me. Thanks!
Classic Event Propagation mistake. You embedded a click handler inside another click handler which reversed the logic of the inner one.
Use event.stopPropagation in your inner handler or rearrange your elements such that they are not nested within one another:
render() {
return (
<div>
<Button
className={classes.button}
variant="contained"
color="secondary"
onClick={this.openDiag.bind(this)}
>
Delete
<Delete className={classes.rightIcon} />
</Button>
{this.state.deleteConfirm ? (
<Child onDelete={this.onDelete.bind(this)} />
) : null}
</div>
);

how to make a callback in reactnative

I have a dialog box that I want to present. I need an arrow function that allows me to call the callback prop that I have written as well as close the modal screen by the use of state. I am able to close it but not invoke the callbacks from the parent component
export class DialogBox extends PureComponent {
okButton = <Button type="text" title={t('action')} onPress={this.onOkPressed} />
cancelButton = <Button type="text" title={t('cancel')} onPress={this.onCancelPressed} />
confirmButtonBox = (
<Fragment>
{this.cancelButton}
{this.okButton}
</Fragment>
)
state = {
isVisible: this.props.isVisible,
}
onModalClose = () => {
this.setIsVisible(false)
}
onOkPressed = () => {
this.props.onOkPress()
this.onModalClose()
}
onCancelPressed = () => {
this.props.onCancelPress()
this.onModalClose()
}
setIsVisible (visible) {
this.setState({ isVisible: visible })
}
renderButtons (type) {
switch (type) {
case 'confirm':
return this.confirmButtonBox
case 'alert':
return this.okButton
default:
return this.okButton
}
}
render () {
const {
title, message, type,
} = this.props
return (
<Modal transparent animationType="fade" visible={this.state.isVisible}>
<View style={styles.container}>
<View style={styles.alertContainer}>
<View>
<Text>
<H3 style={{ fontWeight: 'bold' }}>
{title}
</H3>
</Text>
<Text>
{message}
</Text>
</View>
<View style={styles.buttons}>
{this.renderButtons(type)}
</View>
</View>
</View>
</Modal>
)
}
}
The way I need it to work is:
I have a container class external to this component which allows user to implement a callback method on Ok press and onCancle press.
class DialogBoxStoryContainer extends PureComponent {
// state = { isVisible: false, type: 'confirm' }
onOkPressed = () => {
// this.setState({ isVisible: false })
console.debug('on ok pressed')
}
onCancelPressed = () => {
// this.setState({ isVisible: false })
console.debug('on cancel pressed')
}
render () {
return (
<View>
<DialogBox
{...this.props}
onOkPress={this.onOkPressed}
onCancelPress={this.onCancelPressed}
isVisible="true"
type="confirm"
/>
I do not see the callbacks being executed here. What am i missing here?
Your code works for me. If your "Default Levels" in Chrome (I assume you are using Chrome) is not set for "Verbose", you can't see console.debug outputs. I don't know where this config is in Firefox or other browsers. So, if you are using another one just find this setting.

Close React modal that was imported from other custom modal

First I define this component to reuse with modal windows
Template 1:
...
import Modal from 'react-native-modal';
class CustomModal extends Component {
constructor(props){
super(props);
}
componentWillReceiveProps(nextProps){
this.setState({
visible: nextProps.isVisible
})
}
state = {
visible: false
}
render() {
return (
<TouchableWithoutFeedback
onPress={()=>{
this.setState({
visible: false
});
}}
>
<Modal
isVisible={this.state.visible}
>
<TouchableWithoutFeedback onPress={() => {}}>
<View>
{this.props.content}
</View>
</TouchableWithoutFeedback>
</Modal>
</TouchableWithoutFeedback>
);
}
}
Now in a second component I call it:
import Modal from './common/modal';
return (<Modal
isVisible={this.state.showModal}
content={this.renderMyContent()}
/>
)
Clicking in a button I setState showModal : true, my modal is open, I can click outside the modal and actually the modal disappear but my state.showModal still being : true, how can I update the state in this view?
You can pass through a callback to the onModalHide prop of react-native-modal.
In your CustomModal:
<Modal onModalHide={this.props.onModalHide} ... />
In your "second template"
<Modal onModalHide={() => this.setState({ showModal: false })} ... />.
Note also that there's no need to track visibility within your CustomModal's state, since it already has its visibility in a prop. Just pass the prop down to the inner Modal directly: <Modal isVisible={this.props.isVisible} .../>
Call this.setState({showModal: false}) also in React we refer to these as components not "templates"
Create a function hideModal in the class where you are calling <Modal /> like this:
hideModal = () => {
this.setState({showModal: false})
}
and pass it to the <Modal /> as a prop follows:
import Modal from './common/modal';
return (<Modal
isVisible={this.state.showModal}
content={this.renderMyContent()}
hideModal={this.hideModal}
/>
)
And for closing call the hideModal prop as passed above from your CustomModal component as follows:
...
import Modal from 'react-native-modal';
class CustomModal extends Component {
constructor(props){
super(props);
}
componentWillReceiveProps(nextProps){
this.setState({
visible: nextProps.isVisible
})
}
state = {
visible: false
}
render() {
return (
<TouchableWithoutFeedback
onPress={()=>{
this.props.hideModal()
}}
>
<Modal
isVisible={this.state.visible}
>
<TouchableWithoutFeedback onPress={() => {}}>
<View>
{this.props.content}
</View>
</TouchableWithoutFeedback>
</Modal>
</TouchableWithoutFeedback>
);
}
}

How to pass props from FlatList item to Modal?

I have implemented a View component containing a FlatList, which renders TouchableHighlights. Also I have implemented a Modal component, which I'd like to import at various places including the component that renders the FlatList.
I have already managed to open the modal from outside (via handing over a prop for visibility, accessing it via nextProps and setting modals state value "modalVisible" to this) and closing it from inside (simply via changing it's state value "modalVisible").
BUT: I also want to pass data to the modal from each FlatLists item. An item rendered as a TouchableHighlight should open the modal onPress and the modal should contain data from the item (in the code below this would be the items id). How can I achieve passing data to the modal? I somehow can't get it to work using nextProps. This seems more to be an issue related to setting state from within a FlatLists item rather than the Modal.
Modal:
export default class ModalView extends React.Component {
constructor() {
super();
this.state = {
modalVisible: false,
id: null,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
modalVisible: nextProps.modalVisible,
id: nextProps.id,
})
}
render() {
return (
<Modal
animationType="slide"
transparent={ true }
visible={ this.state.modalVisible }
onRequestClose={() => { this.props.setModalVisible(false) }}
>
<View>
<View>
<Text>{ this.state.id }</Text>
<TouchableHighlight
onPress={() => { this.props.setModalVisible(false) }}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
)
}
}
FlatList rendering TouchableHighlights:
export default class RecentList extends React.Component {
constructor() {
super();
this.state = {
modalVisible: false,
id: null,
}
}
_onPressItem(id) {
this.setState({
modalVisible: true,
id: id,
});
};
_renderItem = ({item}) => {
return (
<TouchableHighlight
id={item.id}
onPress={this._onPressItem}
>
<View>
<Text>{id}</Text>
</View>
</TouchableHighlight>
)
};
render() {
let data = realm.objects('Example').filtered('completed = true')
.sorted('startedAt', true).slice(0, 10)
return (
<View>
<ModalView
modalVisible={ this.state.modalVisible }
setModalVisible={ (vis) => { this.setModalVisible(vis) }}
id={ this.state.id }
/>
<FlatList
data={data}
renderItem={this._renderItem}
keyExtractor={(item, index) => index}
/>
</View>
)
}
}
A small mistake you have missed ...
_renderItem = ({item}) => {
return (
<TouchableHighlight
id={item.id}
onPress={() => this._onPressItem(item.id)} // Your not sending the item.id
>
<View>
<Text>{id}</Text>
</View>
</TouchableHighlight>
)
};

React Native conditionally render part of view while input is focused

I have the following scenario.
function MyComponent() {
return (
<View>
<TextInput ref={ref => (this.input = ref)} style={styles.input} />
{this.input.isFocused() && <Text>Hello World</Text>}
</View>
);
}
This actually works fine, but I get the warning:
MyComponent is accessing findNodeHandle inside its render. render
should be a pure function.
How do I conditionally render The text block and avoid this warning?
You can use component state :
class MyComponent extends React.Component {
state = { isFocused: false }
handleInputFocus = () => this.setState({ isFocused: true })
handleInputBlur = () => this.setState({ isFocused: false })
render() {
const { isFocused } = this.state
return (
<View>
<TextInput
onFocus={this.handleInputFocus}
onBlur={this.handleInputBlur}
/>
{isFocused && <Text>Hello World</Text>}
</View>
)
}
}
You cannot reference refs from the render() method. Read more about the cautions of working with refs here.
As you can see in the image below, the first time the component mounts, refs is undefined, when I change the text (Which changes the State, which re-renders the component) refs is now available.
An optimal solution would be using state. I was going to post a solution but Freez already did. However, I am still posting this so you know why you should be using state instead of refs.

Categories

Resources