how to make a callback in reactnative - javascript

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.

Related

Disable TouchableOpacity after 5 clicks - React Native

I am pulling memes from an API and only want to show 5 memes therefore I tried to disable the touchableOpacity after certian number of clicks. Any ideas on how I can do that?
const link = 'https://meme-api.herokuapp.com/gimme'
class Meme extends React.Component {
constructor() {
super()
this.state = {
loading: false,
data: {
postLink: "sample data",
subreddit: "sample data",
title: "sample data",
url: 'https://poster.keepcalmandposters.com/8170567.jpg'
}
}
}
load = () => {
this.setState({ loading: true })
axios.get(link).then(res => {
this.setState({
data: res.data,
loading: false,
})
console.log(Object.keys(res.data))
})
}
This is where the "button" is
render() {
return (
<View>
<View style={styles.container}>
<View style={styles.imageView}>
<ProgressiveImage source={{ uri: this.state.data.url }} />
</View>
<TouchableOpacity style={styles.button}
onPress={() => {this.load()}} >
<Text style={styles.btnText}>Click to open a meme!</Text>
</TouchableOpacity>
</View>
<AnimatedLoader
visible={this.state.loading}
overlayColor="rgba(255,255,255,0.75)"
source={require("../loader.json")}
animationStyle={styles.lottie}
speed={1} />
</View>
);
}
};
I tried using state/setState for disabiling the button but nothing seems to work. I am okay with just disabiling the button but I tried it with two different pressHandlers, one for this.load() and another for disabling after a click but both of them does not work at the same time.
You need to create a counter state for the button and set the disable properties as well. Here's a simple example for it.
class Meme extends React.Component {
constructor() {
super();
this.state = {
count: 0
};
}
load = () => {
this.setState({ count++ })
// and others logic
}
render() {
return (
<TouchableOpacity onPress={() => {this.load()}} disabled = {this.state.count == 5 ? true : false}>
<Text>Click to open a meme!</Text>
</TouchableOpacity>
);
}
}
functional component example for this.
const Meme = () => {
const [count, setCount] = useState(0)
const loadForAPI = () => {
setCount(count => count + 1)
}
render() {
return (
<TouchableOpacity onPress={() => {loadForAPI()}} disabled = {count == 5 ? true : false}>
<Text>Click to open a meme!</Text>
</TouchableOpacity>
);
}
}
export default Meme;

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

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>
);
}
}

How to call a child method from the parent in React Native?

When a click event is fired within my parent component I need to call the method closeMenu() from the SearchBar child component. I have tried a couple of different ways to do that but none of them are working. Does anyone know how to do this?
class Products extends Component {
constructor(props) {
super(props);
this.state = { closeMenu: false};
this.hideSearchBar = this.hideSearchBar.bind(this);
}
hideSearchBar(e) {
console.log('e: ', React.Children)
this.setState({closeMenu: true});
this.refs.SearchBar.closeMenu();
this.setState({closeMenu: false});
}
componentWillMount() {
this.props.dispatch(getProductList());
}
render() {
const {isLoading, products} = this.props.products;
if (isLoading) {
return <Loader isVisible={true}/>;
}
return (
<TouchableWithoutFeedback onPress={(e) => this.hideSearchBar(e)} style={{zIndex: 0}}>
<View style={styles.wrapper}>
<Header/>
<View style={styles.bodyWrapper}>
<ScrollView style={styles.scrollView}>
<ProductsContainer data={{productsList: { results: products }}}/>
</ScrollView>
<SearchBar ref="SearchBar" style={styles.searchBar} />
</View>
<Footer/>
</View>
</TouchableWithoutFeedback>
);
}
}
I also tried calling closeMenu() without refs:
hideSearchBar(e) {
this.setState({closeMenu: true});
this.SearchBar.closeMenu();
}
Here is the SearchBar component:
class SearchBar extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.suggestions = [];
}
componentWillUpdate(nextProps, nextState) {
console.log("COMPONENT WILL UPDATE");
console.log(nextProps);
console.log(nextState);
}
suggestionClick = (value) => {
}
getSuggestionText = (suggestion) => {
}
onChangeText = (value) => {
this.selectedSuggestion = false
this.props.dispatch(handleSearchItemText(value));
console.log(this.props.products.searchResults);
}
onFocus() {
const {height} = Dimensions.get('window');
this.setState({
contentOffset: {
x: 0,
y: height * 1 / 3
}
});
}
onBlur() {
this.setState({
contentOffset: {x: 0, y: 0}
});
}
closeMenu = () => {
this.props.products.searchResults = {};
}
componentWillReceiveProps() {
if (!this.props.closeMenu) {
this.props.closeMenu = false;
}
}
renderSearches = () => {
this.suggestions = this.props.products.searchResults;
const suggestionTexts = Object.keys(this.props.products.searchResults || {})
console.log(suggestionTexts);
if (!suggestionTexts.length) {
return null
}
// for android absolute element: https://github.com/facebook/react-native/issues/16951
// https://gist.github.com/tioback/6af21db0685cd3b1de28b84981f31cca#file-input-with-suggestions-L54
return (
<View
ref="suggestionsWrapper"
style={autoStyles.suggestionsWrapper}
>
{
this.suggestions.map((text, index) => (
<TouchableHighlight
key={index}
suggestionText={text}
activeOpacity={0.6}
style={autoStyles.suggestion}
onPress={this.suggestionClick(this.suggestions[text])}
underlayColor='white'
>
<Text style={autoStyles.suggestionText}>
{text}
</Text>
</TouchableHighlight>
))
}
</View>
)
}
render() {
const myIcon = (<Icon name="search" size={30} style={styles.searchIcon}/>);
const slidersIcon = (<Icon name="sliders" size={30} style={styles.slidersIcon}/>);
return (
<TouchableWithoutFeedback style={{zIndex: 0}}>
<View style={[styles.searchBar, this.props.style]}>
<View style={styles.searchContainer}>
<View>
{slidersIcon}
</View>
<View style={styles.search}>
<View style={styles.searchSection}>
{myIcon}
<TextInput
style={styles.input}
placeholder="Search"
placeholderTextColor="rgba(0,0,0,0.7)"
onChangeText={(searchString) => {
this.setState({searchString})
}}
underlineColorAndroid="transparent"
editable={true}
autoCorrect={false}
autoFocus={false}
autoCaptialize={'none'}
autoCorrect={false}
onChangeText={this.onChangeText}
enablesReturnKeyAutomatically={true}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
/>
</View>
</View>
</View>
{this.renderSearches()}
</View>
</TouchableWithoutFeedback>
);
}
}
There are some issues which you should avoid:
Never mutate props: this.props.something = {} is an anti-pattern. Think about props as data that your component does not own and which are not mutable. If they change then only because the parent passed new props.
Also you have multiple handlers in your SeachBar that are not bound to this but use this. It will not work. Use arrow functions if you want to use this or bind them in the constructor.
You should overthink the architecture of your app. Maybe it is a good idea to split the search bar and the result list into two separate components. When the user types something to search for update your redux store and display the results in a separate component that you only render if there are search results.
I'm affraid it would exceed the length of a stackoverflow answer to solve all these issues. Maybe you should go back to the basics first and do the really good redux tutorial.

Navigate to another screen from Flat list item getting pressed

I've been using wix/react-native-navigation package to navigate between screens and handling the stack properly.
Moving across screens is pretty straightforward, firing those transitions when a button gets pressed. But the issue comes up when I have a FlatList and I want to push to a new screen when the user taps an item from the list, looks like the navigator props injected at the beginning is lost or in another context than the onPress callback event;
Here is the sample code
class AlertType extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.itemId, this.props.itemName, this.props.itemImageUrl);
};
render() {
return (
<TouchableOpacity { ...this.props }
onPress={ this._onPress }
style={ itemStyle.cardContainer }>
<View style={ itemStyle.mainContainer }>
<View style={{ width: 10 }}/>
<Image
source={{ uri: NET.HOST + this.props.itemImageUrl }}
style={{ width: 45, height: 45 }}
/>
<View style={{ width: 10 }}/>
<Text style={ itemStyle.itemText }>{ this.props.itemName }</Text>
</View>
</TouchableOpacity>
);
}
}
class AlertsScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
alertTypes: null,
}
}
_onAlertTypePressed(typeId: string, typeName: string, imageUrl: string){
this.props.navigator.push({
screen: 'prod.screens.AlertsCreator',
title: 'Alert',
passProps: {
alertId: typeId,
alertName: typeName,
alertImage: imageUrl
}
});
}
_renderListItem = ({ item }) => (
<AlertType
itemName={ item.titulo }
itemId={ item.key }
itemImageUrl={ item.url }
onPressItem={ this._onAlertTypePressed }
/>
);
render() {
return (
<View style={ styles.mainContainer }>
<FlatList
data={ this.state.alertTypes }
ItemSeparatorComponent={ () => <View style={{ height: 5 }}/> }
renderItem={ this._renderListItem }
/>
</View>
);
}
const mapSessionStateToProps = (state, ownProps) => {
return {
session: state.session
};
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(mapSessionStateToProps, mapDispatchToProps)(AlertsScreen)
This approach produces the next error
There have to be something I'm missing, I know this.props.navigator is not undefined, but inside on _onAlertTypePressed the navigator prop is undefined.
The problem is that you pass function to component without binding it to the current context.
You should pass:
this._onAlertTypePressed.bind(this);
another approach is binding your functions in the constructor:
constructor(props) {
this._onAlertTypePressed = this._onAlertTypePressed.bind(this);
}
I've had this happen before also.
I had to declare navigator between the render and return blocks
render() {
const navigator = this.props.navigator
return()}}
then pass navigator through when calling _onAlertTypePressed
() => _onAlertTypePressed(navigator)
then use navigator vs this.props.navigator inside _onAlertTypePressed

how to show a component depending of picker value selected? [React native]

I'm trying to make a game with react native and I want to show a different options when i change the picker value.
basically when I select the first option on the picker a component has to appear and when I select the second one another component.
I tried this function but not working
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
here is my code
export default class Facil extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true,
PickerValueHolder : '',
filter: [
{
"option":"Palabras por categoria"
},
{
"option":"Palabras por caracteres"
}
],
dataSource:[]
}
}
componentDidMount() {
return fetch(API_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
const resizeMode = 'stretch';
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
return (
<View style={styles.container}>
<Image source={require('../../Images/HomeLayout.png')}
style={styles.imagen}
/>
<View style={styles.mView}>
<View style={styles.panel}>
<Text style={styles.titlePanel}>MODO FACIL</Text>
<Text style={styles.instructions}>{instructions[0]}</Text>
<View style={styles.picker}>
<Picker
selectedValue={this.state.PickerValueHolder}
style={ {height: '100%',width: '100%'}}
mode="dropdown"
onValueChange={(itemValue, itemIndex) => this.setState({PickerValueHolder: itemValue})} >
{ this.state.filter.map((item, key)=>(
<Picker.Item label={item.option} value={item.option} key={key} />)
)}
</Picker>
</View>
<View style={styles.gameOpt}>
<Text>[dynamic options]</Text>
{pickerOptionText}
</View>
</View>
</View>
<TouchableOpacity style={styles.button}><Text style={styles.btnText}>Play!</Text></TouchableOpacity>
</View>
);
}
}
You forgot '()'.
pickerOptionText is a function, not a React component.
<Text>[dynamic options]</Text>
{pickerOptionText}
to:
<Text>[dynamic options]</Text>
{pickerOptionText()}
You can try using Conditional Rendering of JSX, by this you can use ternary operator and a simple if condition. this is written as:
{this.state.PickerValueHolder==this.state.filter[0] ?
<Text>{instructions[2]}</Text>
:<Text>{instructions[1]}</Text>
}
and if you need simple if condition then,
{ condition == true && <Text>your text here</Text>
}

Categories

Resources