How to get reference of TextInput which define in static Navigation Options? - react-native

I am facing problem getting reference of TextInput which is defined in static navigationOptions, it's that any way to do it in React Native? i am sharing some code here so you better know about my questions.
export default class SearchClass extends React.Component {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
let headerTitle = (
<View style={{flex: 1}}>
<TextInput
style={{ flex: 1, height: 30 }}
placeholder='Search'
keyboardType='default'
returnKeyType='search'
underlineColorAndroid='transparent'
onChangeText={text => params.onChangeText(text)}
onSubmitEditing={(event) => { params.onSearchButton()
}}
/>
</View>
);
return { headerTitle };
};
componentDidMount() {
this.props.navigation.setParams({
onChangeText: this.__onChangeSearchText,
onSearchButton: this.__onSearchButtonPress
});
}
__onSearchButtonPress = () => {
// clear the text of TextInput
// for that i need reference here of TextInput
this.mySearchBox.clear();
}
}
_onSearchButtonPress i need to clear the text of TextInput, for that i need reference of that TextInput which is define in navigationOptions ?
does anyone know how to do it?

I've added a static variable inside class
static textField = null;
and then assign it inside navigationOptions
<TextInput
ref={(ref) => YourClass.textField = ref}
...
then when I need to pass events (not sure if that's the best way) I either do a pubsub-js listener and publish event or I forward events - both ways explained with code bellow.
<TextInput
ref={(ref) => YourClass.textField = ref}
onChangeText={(text) => {
YourClass.textField.onChangeText(text);
// PubSub.publish(Constants.FIELD_DID_CHANGE, [text,]);
}}
then when didFocus -- viewDidAppear equivalent
let textField = YourClass.textField;
textField.onChangeText = (text) => this._textFieldDidChange(text);
or register PubSub object in componentDidMount
// this.textFieldDidChangeListener = PubSub.subscribe(Constants.FIELD_DID_CHANGE, (msg, data) => this._textFieldDidChange(data));

Related

TextInput focus blurring when first clicked

There was no issue before I tried to implement the handleInputBlur and handleInputFocus functions (Used to change the background when in focus). When I first click on the TextInput it comes into focus, but then immediately blurs, resulting in the background flashing then disappearing. What's strange is that after this first click, the future clicks work absolutely fine, focusses and blurs as it should. I do not understand why on the initial click/focus it immediately blurs. Code below:
EDIT: Bit more context, it's inside of a modal, which contains multiple of these editable items.
class EditableItem extends Component {
constructor(props) {
super(props)
const { value } = this.props
this.state = {
value,
isFocused: null,
}
}
handleInputBlur = () => {
this.setState({ isFocused: false })
console.log('blurring')
}
handleInputFocus = () => {
this.setState({ isFocused: true })
console.log('focussing')
}
render() {
const { name, secure, children, autoCapitalize } = this.props
const { value, isFocused } = this.state
const multiline = !secure
return (
<View>
<View style={styles.container}>
<Text style={styles.name}>{name}</Text>
<View style={isFocused ? styles.activeBackground : styles.unfocusedBackground}>
<TextInput
placeholder={name}
placeholderTextColor={COLOR_BASE_3}
underlineColorAndroid="transparent"
style={styles.value}
secureTextEntry={secure}
value={value}
// blurOnSubmit
onSubmitEditing={() => {
Keyboard.dismiss()
}}
returnKeyType="done"
keyboardAppearance="dark"
autoCapitalize={autoCapitalize}
onChangeText={this.onChange}
multiline={multiline}
onBlur={() => this.handleInputBlur()}
onFocus={() => this.handleInputFocus()}
/>
{children}
</View>
</View>
<View style={styles.divider} />
</View>
)
}
onChange = value => {
const { onChange } = this.props
this.setState({ value })
onChange(value)
}
}
Ok so solved this by setting autofocus to true in the TextInput. Not sure why not having this set causes this issue but it's a solution regardless.

React Native - Rerunning the render method

I have a file here that defines an icon for the title.
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (<HomeHeaderIcon/>)
}
};
HomeHeaderIcon.js
export default class HomeHeaderIcon extends Component {
async componentDidMount(){
const token = await AsyncStorage.getItem('token');
this.setState({token});
}
state={
token:null
};
render() {
return (
<View>
{
this.state.token ===null
?
(
<TouchableOpacity
onPress={() => (NavigationService.navigate("LogStack"))}>
<Icon name="ios-power" size={30} style={{color: "white",marginRight:wp("5%")}}/>
</TouchableOpacity>
)
:
(
<TouchableOpacity
onPress={() => (NavigationService.navigate("Profile"))}>
<Icon name="ios-home" size={30} style={{color: "white",marginRight:wp("5%")}}/>
</TouchableOpacity>)
}
</View>
);
}
}
The system works exactly as I want. If there is a token, I say icon1 or icon2 show. The problem is I do this in componentDidMount, the icon does not change without refreshing the page. How do I render it again?
componentDidMount is called, as the name suggests, just once, when the component is mounted. Use componentDidUpdate to decide how your component behaves based on what piece of props or state has changed.
Read the documentation for more information regarding lifecycle methods.

Application wide Modal in React Native

I'm currently using react native modal and it serves the purpose of showing modals.
My problem currently is that I want to show the modal application wide. For example when a push notification received I want to invoke the modal regardless of which screen user is in. The current design of the modals bind it to a single screen.
How can this be overcome?
first of all make a context of your modal
const BottomModal = React.createContext();
then provide your modal using reactcontext provider
export const BottomModalProvider = ({children}) => {
const panelRef = useRef();
const _show = useCallback((data, type) => {
panelRef.current.show();
}, []);
const _hide = useCallback(() => {
panelRef.current.hide();
}, []);
const value = useMemo(() => {
return {
_show,
_hide,
};
}, [_hide, _show]);
return (
<BottomPanelContext.Provider value={value}>
{children}
<BottomPanel fixed ref={panelRef} />
</BottomPanelContext.Provider>
);
};
here is code for bottom panel
function BottomPanel(props, ref) {
const {fixed} = props;
const [visible, setVisibility] = useState(false);
const _hide = () => {
!fixed && hideModal();
};
const hideModal = () => {
setVisibility(false);
};
useImperativeHandle(ref, () => ({
show: () => {
setVisibility(true);
},
hide: () => {
hideModal();
},
}));
return (
<Modal
// swipeDirection={["down"]}
hideModalContentWhileAnimating
isVisible={visible}
avoidKeyboard={true}
swipeThreshold={100}
onSwipeComplete={() => _hide()}
onBackButtonPress={() => _hide()}
useNativeDriver={true}
style={{
justifyContent: 'flex-end',
margin: 0,
}}>
<Container style={[{flex: 0.9}]}>
{!fixed ? (
<View style={{flexDirection: 'row', justifyContent: 'flex-end'}}>
<Button
style={{marginBottom: 10}}
color={'white'}
onPress={() => setVisibility(false)}>
OK
</Button>
</View>
) : null}
{props.renderContent && props.renderContent()}
</Container>
</Modal>
);
}
BottomPanel = forwardRef(BottomPanel);
export default BottomPanel;
then wrap your app using the provider
...
<BottomModalProvider>
<NavigationContainer screenProps={screenProps} theme={theme} />
</BottomModalProvider>
...
lastly how to show or hide modal
provide a custom hook
const useBottomPanel = props => {
return useContext(BottomPanelContext);
};
use it anywhere in app like
const {_show, _hide} = useBottomModal();
//....
openModal=()=> {
_show();
}
//...
If you are not using hooks or using class components
you can easily convert hooks with class context
https://reactjs.org/docs/context.html#reactcreatecontext
this way you can achieve only showing the modal from within components
another way is store the panel reference globally anywhere and use that reference to show hide from non-component files like redux or notification cases.

Undefined is not an object (evaluating 'navigation.getParam')

React-native, react-navigation. I have a custom header, with a button, that I've extracted to another file. I can't seem to get access to the navigation object.
function SetNavOptions(title, navigation) {
const { buttonStyle, iconStyle } = styles;
return {
headerBackground: (<LinearGradient
colors={['#337ab7', '#265a88']}
style={{ flex: 1 }}
start={[0, 0]}
end={[0, 1]}
/>),
headerTitleStyle: {
color: 'white',
},
headerTitle: title,
headerLeft: (
<TouchableOpacity onPress={() => navigation.getParam('mydrawer')} style={buttonStyle}>
<View>
<Icon.FontAwesome
name='bars'
size={26}
style={iconStyle}
/>
</View>
</TouchableOpacity>
),
};
}
And this is my screen where I set the navigationOptions variable:
export default class HomeScreen extends React.Component {
static navigationOptions = SetNavOptions('Topics', this.navigation);
componentDidMount() {
this.props.navigation.setParams({ mydrawer: this.openDrawer });
}
openDrawer = () => {
this.props.navigation.navigate('myDrawer');
};
}
Running the code and clicking the button, I get 'undefined is not an object (evaluating 'navigation.getParam'). What am I doing wrong? Also, is how I defined SetNavOptions() the best way to extract all of that? I'm new to react-native.
The problem was that my static navigationOptions line was not correct in the HomeScreen class. It should be this instead:
static navigationOptions = ({ navigation }) => SetNavOptions('Topics', navigation)
Had similar problem, and route.params.mydrawer instead of navigation.getParam('mydrawer') solved the issue. Looks like there was an update with React Native router, because before it warked with navigation.getParam.
Inspired by https://stackoverflow.com/a/64932908/11127383

React Native Navigation - Action using Component's State

I've made a full-screen TextInput and would like to have an action performed when the Post button in the NavigationBar is pressed. However, because I have to make the method that the Button is calling in the onPress prop a static method, I don't have access to the state.
Here is my current code, and the state comes up undefined in the console.log.
import React, { Component } from 'react';
import { Button, ScrollView, TextInput, View } from 'react-native';
import styles from './styles';
export default class AddComment extends Component {
static navigationOptions = ({ navigation }) => {
return {
title: 'Add Comment',
headerRight: (
<Button
title='Post'
onPress={() => AddComment.postComment() }
/>
),
};
};
constructor(props) {
super(props);
this.state = {
post: 'Default Text',
}
}
static postComment() {
console.log('Here is the state: ', this.state);
}
render() {
return (
<View onLayout={(ev) => {
var fullHeight = ev.nativeEvent.layout.height - 80;
this.setState({ height: fullHeight, fullHeight: fullHeight });
}}>
<ScrollView keyboardDismissMode='interactive'>
<TextInput
multiline={true}
style={styles.input}
onChangeText={(text) => {
this.state.post = text;
}}
defaultValue={this.state.post}
autoFocus={true}
/>
</ScrollView>
</View>
);
}
}
Any ideas how to accomplish what I'm looking for?
I see you've found the solution. For future readers:
Nonameolsson posted how to achieve this on Github:
In componentDidMount set the method as a param.
componentDidMount () {
this.props.navigation.setParams({ postComment: this.postComment })
}
And use it in your navigationOptions:
static navigationOptions = ({ navigation }) => {
const { params } = navigation.state
return {
title: 'Add Comment',
headerRight: (
<Button
title='Post'
onPress={() => params.postComment()}
/>
),
};
};
Kinda like a hack but i use the global variable method where we assign this to a variable call foo. Works for me.
let foo;
class App extends Component {
static navigationOptions = ({ navigation }) => {
return {
title: 'Add Comment',
headerRight: (
<Button
title='Post'
onPress={() => foo.postComment() } <- Use foo instead of this
/>
),
};
};
componentWillMount() {
foo = this;
}
render() {
return (<div>Don't be a foo</div>)
}
}