I have to have an uncontrolled text input (for some reason not detailed here). I am trying to block a change from happening in my <TextInput>.
Here is my component and here is Snack demo of it - https://snack.expo.io/#noitsnack/textinput-block-onchange
class UncontrolledInput extends Component {
inputRef = null
render() {
return (
<View>
<TextInput ref={this.refInput} onChange={this.handleChange} />
</View>
)
}
refInput = el => this.inputRef = el
handleChange = e => {
const { nativeEvent:{ text } } = e;
// block i characters
if (text.includes('i')) {
e.preventDefault();
e.stopPropagation();
e.returnValue = false;
return false;
}
}
}
Is this possible? While preventDefault and stopPoropagation exist in e they don't seem to do anything.
You just want the text not to update in certain situations? You can always bind it to a state value and determine whether the state value should update:
class UncontrolledInput extends Component {
state = {
text: '',
}
render() {
return (
<View>
<TextInput value={this.state.text} onChangeText={this.handleChange} />
</View>
)
}
handleChange = text => {
// only update if no 'i'
if (!text.includes('i')) {
this.setState({ text });
}
}
}
Related
My task is to show the download component when data from the server has not yet arrived.
export const LoaderComponent = () => (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
const styles = StyleSheet.create({
center: {
.....
},
});
I created a state file to display the boot component.
import { observable, action } from 'mobx';
class LoaderState {
#observable loading: boolean = true;
#action showLoader() {
this.loading = true;
}
#action hideLoader() {
this.loading = false;
}
}
export default new LoaderState();
When authorizing the user, I display the download component, after receiving data from the server, I hide the download component. I made an artificial delay of two seconds.
class AuthState {
#observable email: string = '';
#observable password: string = '';
#action authentication(data: IAuth) {
console.log('Action authentication');
LoaderState.showLoader();
....
setTimeout(() => {
LoaderState.hideLoader();
console.log('Change state loader', LoaderState.loading);
}, 2000);
}
}
export default new AuthState();
On the next screen, I check if the download flag is set, I show the download component, and if not, I hide it.
export const ProvidersScreen = () => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
};
The problem is that the download component is always shown and when the state changes, it is not hidden. Why is the download component not hiding?
I think the reason is your ProvidersScreen is not an observer component, so try it:
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});
You forgot to add observer
Add below code:
import { observer } from "mobx-react";
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});
I have a TextInput component gets reused in a few different places:
export default class SomeTextInput extends Component {
constructor(props) {
super(props);
}
render() {
let fontWeight = this.props.fontWeight ? this.props.fontWeight : 'Light';
let fontName = this.props.fontName ? this.props.fontName : 'Montserrat';
let fontString = createFontString(fontName, fontWeight);
let applyFontFamily = { fontFamily: fontString };
let style = this.props.style.constructor === Array ? this.props.style : [this.props.style];
return (
<TextInput
ref={(ref) => {
this.textInput = ref
}}
{...this.props}
style={[applyFontFamily, ...style]}
onFocus={() => {
this.clearText();
console.log('show me this.textInput', this.textInput.props.placeholder)
}}
/>
)
}
clearText() {
this.textInput.clear();
console.log('is this being reached???')
}
focus() {
this.textInput.focus();
}
blur() {
this.textInput.blur();
}
}
I've also tried using clearTextOnFocus. I believe the best way to do this would be to change the placeholder to '', but I'm not sure how given that the placeholder text is taken from a prop that's been passed down.
edit: I'm going to add the code that #ravibagul91 suggested
export default class OVTextInput extends Component {
constructor(props) {
super(props);
// this.state = {
// placeholder: props.placeholder
// }
}
render() {
let fontWeight = this.props.fontWeight ? this.props.fontWeight : 'Light';
let fontName = this.props.fontName ? this.props.fontName : 'Montserrat';
let fontString = createFontString(fontName, fontWeight);
let applyFontFamily = { fontFamily: fontString };
let style = this.props.style.constructor === Array ? this.props.style : [this.props.style];
return (
<TextInput
ref={(ref) => {
this.textInput = ref
}}
{...this.props}
style={[applyFontFamily, ...style]}
onFocus={() => {
// this.setState({ placeholder: "" });
this.clearText();
}}
/>
)
}
clearText = () => {
console.log(this.textInput)
console.log('is this being reached???', this.textInput.value);
console.log('is this being reached???', this.textInput.placeholder);
this.textInput.placeholder = "";
this.textInput.value = "";
}
// focus = () => {
// this.textInput.focus();
// }
// blur = () => {
// this.textInput.blur();
// }
focus() {
this.textInput.focus();
}
blur() {
this.textInput.blur();
}
};
What you are currently doing is erasing the value of the text. Your Textinput looks like a prop for receiving and using values. Textinput does not currently have the ability to clear placeholders. If you make a proposal, you can use the status values to solve it.
export default class SomeTextInput extends Component {
constructor(props) {
super(props);
this.state={
placeholder: props.placeholder
}
}
....
<TextInput
ref={(ref) => {
this.textInput = ref
}}
placeholder={this.state.placeholder}
{...this.props}
style={[applyFontFamily, ...style]}
onFocus={() => {
this.setState({ placeholder : "" });
console.log('show me placeholder', this.state.placeholder)
}}
/>
You can directly clear the placeholder like,
this.textInput.placeholder = "";
Demo
Note: This is tested simply on input but same will work for TextInput.
I have an App with a navigationbar with 2 Screens.
When i apply a function on Screen/Component 1 , I want to render or trigger a change in the Second Screen.
is there a way to either re-render the screen on Enter or to update the state of the other screen ?
Component one:
export default class HomeScreen extends React.Component {
constructor() {
super();
}
_onPress(){
try {
await AsyncStorage.setItem('value', 'changed Value');
} catch (error) {
console.log(error.message);
}
console.log("saved: " + this.state.userName )
}
render() {
return (
<View style={styles.container}>
<Button title="btn" onPress={() => this._onPress()} >
</Button>
</View>
)
}
component 2:
export default class SecondScreen extends React.Component {
constructor() {
super();
this.state = {some : ''}
}
async getValue () {
let recievedValue = '';
try {
let promise = await AsyncStorage.getItem('value') || 'cheeseCake';
promise.then((value) => recievedValue = value)
} catch (error) {
// Error retrieving data
console.log(error.message);
}
return recievedValue
}
render() {
var value= this.getValue();
return (
<View style={styles.container}>
<Text>
HERE CHANGED VALUE: {value}
</Text>
<Button onPress={()=> this.setState((prev)=> {some:'Thing'})}>
</Button>
</View>
)
}
When i press the Button on screen 1(HomeScreen) the value is saved.
But it only shows in the secont screen when I trigger a statechange via Button Press.
How do I render the screen when I visit the screen via navigation bar ?
Did you try EventEmiter?
Use this custom event listener: https://github.com/meinto/react-native-event-listeners
eg:
import { EventRegister } from 'react-native-event-listeners'
/*
* RECEIVER COMPONENT
*/
class Receiver extends PureComponent {
constructor(props) {
super(props)
this.state = {
data: 'no data',
}
}
componentWillMount() {
this.listener = EventRegister.addEventListener('myCustomEvent', (data) => {
this.setState({
data,
})
})
}
componentWillUnmount() {
EventRegister.removeEventListener(this.listener)
}
render() {
return <Text>{this.state.data}</Text>
}
}
/*
* SENDER COMPONENT
*/
const Sender = (props) => (
<TouchableHighlight
onPress={() => {
EventRegister.emit('myCustomEvent', 'it works!!!')
})
><Text>Send Event</Text></TouchableHighlight>
)
I am trying to create a changing pin screen and i was failed in comparing two variable that getting from the user (new pin and comfirm pin). The error show me that "this.state.newpin" is an undefined object.
class SettingScreen extends Component {
state = {
oldpin: '000000',
newpin: '',
secpin: ''
}
onPressButton(){
if( this.state.newpin == this.state.secpin){
ToastAndroid.show("Password Changed", ToastAndroid.SHORT);
this.setState({ oldpin : this.state.newpin})
}
else {
ToastAndroid.show("Password Unmatched", ToastAndroid.SHORT);
}
}
handleNewPin = (text) => {
this.setState({ newpin: text })
}
handleSecPin = (text) => {
this.setState({ secpin: text })
}
...
<TextInput onChangeText = {this.handleNewPin} />
<TextInput onChangeText = {this.handleSecPin} />
<TouchableOpacity onPress={this.onPressButton}>
<Text> Change Password </Text>
</TouchableOpacity>
I can get the output for "this.state.newpin" and "this.state.secpin" from user.
I just failed in the comparing statement ( OnPressButton()).
I am new in React-Native.
Sorry for any inconvenience.
you just need to bind your onPressButton()func. in the constructor with this. and move your state to constructor like this;
class SettingScreen extends Component {
constructor(props) {
super(props);
this.state = {
oldpin: '000000',
newpin: '',
secpin: ''
};
this.onPressButton = this.onPressButton.bind(this);
}
}
Is there any way to access a row using key, set using keyExtractor in FlatList.
I using FlatList inorder to populate by data, i need to get a row separately using it's key, inorder to update that row without re-render the entire view.
on componentWillMount i populated datalist array using an api call.
dummy array look this
[{id:"Usr01",title:"Name1"},{id:"Usr02",title:"Name2"},...]
while press on any row i get it's id, i need to access that row using it's key.
let dataitem = this.state.datalist[id];
while i console dataitem i get undefined
i set id as the key in keyExtractor, is there any way to do the same.
My code look like this
FlatListScreen.js
export default class FlatListScreen extends Component {
constructor(props)
{
super(props);
this.state={
datalist: [],
}
}
componentWillMount() {
ApiHandler.getlistitem('All').then(response =>{
this.setState({datalist: response});
});
}
_keyExtractor = (item, index) => item.id;
_onPressItem = (id) => {
let dataitem = this.state.datalist[id];
const { name } = dataitem
const newPost = {
...dataitem,
name: name+"01"
}
this.setState({
datalist: {
...this.state.datalist,
[id]: newPost
}
})
};
_renderItem ({ item }) {
return (
<MyListItem
id={item.id}
onPressItem={this._onPressItem}
title={item.title}
/>
)
}
render() {
return (
<FlatList
data={this.state.datalist}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
}
MyListItem.js
export default class MyListItem extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
id: ''
}
}
componentWillMount() {
const { title, id } = this.props
this.setState({ title, id })
}
componentWillReceiveProps(nextProps) {
const { title, id } = nextProps
this.setState({ title, id })
}
shouldComponentUpdate(nextProps, nextState) {
const { title} = nextState
const { title: oldTitle } = this.state
return title !== oldTitle
}
render() {
return (
<View>
<TouchableOpacity onPress={() =>this.props.onPressItem({id:this.state.id})}>
<View>
<Text>
{this.props.title}
</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
I think changing
onPressItem({id:this.state.id});
to
onPressItem(this.state.id); in your child component
OR
_onPressItem = (id) => { }
to
_onPressItem = ({id}) => { }
in your parent component will solve the issue.
As you are sending it as an object from child to parent and you can access it like this also
let dataitem = this.state.datalist[id.id];