I am trying to set an Int value from the state inside a <TextInput> by first turning it into string (<TextInput> can only receive a string) and then I want to be able to change the value and update the state value with the new <TextInput/> value. When I am trying to change the value inside the <TextInput/>
I get error:
undefined is not an object (evaluating 'this.state.keys.toString')
UPDATE:
I removed the this from this.keyInt and now I receive NaN on input update , the error is gone though
React-Native code:
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
keys: 0,
id: this.props.id
};
}
updateKeysWithInputHandler = (e) => {
keyInt = parseInt(e.target.value);
console.log(`keyInt is: ${keyInt}`);
this.setState({
keys: keyInt
})
}
render() {
return (
<View style={styles.container}>
<TextInput
id={this.props.id}
style={styles.title}
keyboardType='numeric'
maxLength={2}
value={this.state.keys.toString()}
onChange={this.updateKeysWithInputHandler}
/>
</View>
);
}
Ok I fixed my problem thanks to this post and the help from Adam Azad which point me to my problem.
apparently react-native <TextInput/> dont use target and instead its use nativeEvent.text so I changed my code and it worked now.
Working code:
updateKeysWithInputHandler = (val) => {
keyInt = parseInt(val);
console.log(val);
this.setState({
keys: keyInt
})
}
render() {
return (
<View style={styles.container}>
<TextInput
id={this.props.id}
style={styles.title}
keyboardType='numeric'
maxLength={2}
value={this.state.keys.toString()}
onChange={(event) => this.updateKeysWithInputHandler(event.nativeEvent.text)}
/>
</View>
);
}
}
Why keys: this.keyInt?
Shouldn't it be keys: keyInt?
this.setState({
keys: this.keyInt
// ___^^^^^
})
Remove the this keyword because it changes the context from the current scope to Counter scope, where keyInt variable is not defined.
updateKeysWithInputHandler = (event) => {
if (isNaN(event.target.value) || event.target.value.toString().length===
0) {
event.target.value = 0;
}
this.setState({
keys: parseInt(event.target.value)
})
}
Related
I've got a useState variable that has an array of JSON objects, and I'm trying to get the text fields to dynamically render and update with onChangeText, but I'm having a bit of an issue with this.
When I add console.log(index) to updateHashtags, it says it is undefined, and I can't understand what I'm doing wrong or can do to make this work. In theory, the index should be a static number for each text field. So the first text field would have hashtags[0] as its value and use '0' as the index for updating the state of hashtags.
When I use:
console.log('index',index);
inside of updateHashtags, I get:
index undefined
Here's the code:
const updateHashtags = (text, index) => {
let ht = hashtags;
ht[index].name = text;
setHashtags(ht);
}
const hashtagElement = (
<>
<Text style={styles.plainText} >Set Your Values:</Text>
<Text style={styles.instructionsText} >This is what other users use to search for you.</Text>
{hashtags.map((e,index) =>
<TextInput
placeholder='value'
key={e.name + index}
value={hashtags[index].name}
onChangeText={(text,index) => updateHashtags(text,index)}
style={styles.textInput}
/>
)}
<TouchableOpacity
onPress={() => {
let ht = hashtags;
let newht = {
name: '',
weight: 0,
};
ht[ht.length] = newht;
setHashtags(ht);
}}
>
<Ionicons
name="add-circle-outline"
size={40}
color={'black'}
/>
</TouchableOpacity>
</>
);
Maybe the onChangeText functions doens't get index param.
Try it like this:
// we are getting index from here
{hashtags.map((e,index) =>
<TextInput
placeholder='value'
key={e.name + index}
value={hashtags[index].name}
// so no need of taking index from param here
onChangeText={(text) => updateHashtags(text,index)}
style={styles.textInput}
/>
)}
Also for your updateHashTags functions consider this insted:
const updateHashtags = (text, index) => {
// doing it this way ensures your are editing updated version of state
setHashtags((state) => {
let ht = state;
ht[index].name = text;
return ht;
});
}
Hi I'm wondering why this code works,
Sorry for the sintax errors, this is an Example. My question is why memberIpAssignments is taking ip value?. I don't get it if I'm no passing ip into setMemberIpAssignments(arr =>[...arr]) but still takes that's value and updating the state.
setMemberIpAssignments(arr =>[...arr]), this state shouldn't change at all, because I'm no giving ip value. But it does change taking ip value.
if someone can explain to me I'll be grateful.
I'm new at react-native
export const zeroTierNetworkMembersUpdateScreen = ({ route }) => {
const { ipAssignments } = ["192.168.0.1","192.168.1.1"];
const [memberIpAssignments, setMemberIpAssignments] =(ipAssignments);
return (
<View style={styles.viewSet}>
{memberIpAssignments.map((eachIpAssignments, index) => {
return (
<Input
key={index}
placeholder={"ipAssignments"}
keyboardType={"default"}
value={eachIpAssignments}
onChangeText={(value) => {
var ip = ipAssignments;
ip[index] = value;
setMemberIpAssignments(arr =>[...arr]);
}}
/>
);
})}
</View>
);
};
I think I've confirmed my suspicions that you are in fact mutating an object reference that you've stored in local component state.
export const zeroTierNetworkMembersUpdateScreen = ({ route }) => {
// (1) ipAssignments array reference
const ipAssignments = ["192.168.0.1", "192.168.1.1"];
// (2) memberIpAssignments references ipAssignments
const [memberIpAssignments, setMemberIpAssignments] = useState(ipAssignments);
return (
<View style={styles.viewSet}>
{memberIpAssignments.map((eachIpAssignments, index) => {
return (
<Input
key={index}
placeholder={"ipAssignments"}
keyboardType={"default"}
value={eachIpAssignments} // (3) value from memberIpAssignments
onChangeText={(value) => {
// (4) ip references ipAssignments & memberIpAssignments
var ip = ipAssignments;
// (5) element mutation!!
ip[index] = value;
// (6) state update to trigger rerender
setMemberIpAssignments(arr => [...arr]);
}}
/>
);
})}
</View>
);
};
As far as I can tell the mutation happens exactly once since initially everything is reference the original ipAssignments array. Upon updating state though, arr => [...arr] is returning a new array reference for memberIpAssignments the references back to ipAssignments is broken.
You should really be using a functional state update to "edit" the ip entry any way. Consider the following:
export default function App() {
const ipAssignments = ['192.168.0.1', '192.168.1.1'];
const [memberIpAssignments, setMemberIpAssignments] = React.useState(
ipAssignments
);
return (
<View>
{memberIpAssignments.map((eachIpAssignments, index) => {
return (
<TextInput
key={index}
placeholder={'ipAssignments'}
keyboardType={'default'}
value={eachIpAssignments}
onChangeText={(value) => {
setMemberIpAssignments((arr) =>
arr.map((el, i) => (i === index ? value : el))
);
}}
/>
);
})}
</View>
);
}
Expo Snack
What I'm trying to achieve:
I have many forms and I want to keep form validation logic in my HOC so that I don't have to repeat validation logic as most forms would have same fields and some extra or less fields.
How I have Implemented:
Learning to create HOC, followed this example HOC example and tried to create an HOC like below.
import React from 'react';
import {
spaceCheck,
specialCharacterCheck,
numberValidator
} from '../../utility/validators';
const fieldHOC = (WrappedComponent) => {
class HOC extends React.Component {
state = {
error: {
name_first: {
fieldType: 'name_first',
errorType: 0
},
name_last: {
fieldType: 'name_last',
errorType: 0
},
email: {
fieldType: 'email',
errorType: 0
}
}
};
getErrorMessage = (fieldType, errorType) => {
this.setState({
error: {
...this.state.error,
[fieldType]: {
...this.state.error[fieldType],
errorType
}
}
});
};
checkFieldsError = (currentFocus, nextFocus) => {
//Not able to get props passed by below component in class functions
console.log('MY PROPS', this.props);
const field = this.props[currentFocus];
if (field === '' || spaceCheck(field)) {
this.getErrorMessage(currentFocus, 1);
} else if (specialCharacterCheck(field)) {
this.getErrorMessage(currentFocus, 2);
} else if (numberValidator(field) || numberValidator(field)) {
this.getErrorMessage(currentFocus, 3);
} else {
this.setState({
error: {
...this.state.error,
[currentFocus]: {
...this.state.error[currentFocus],
errorType: 0
}
}
});
}
this[nextFocus].focus();
}
render() {
const { children } = this.props;
// Here able to access props(name_first, name_last and email) passed from below component
// console.log('PROPS', this.props);
return (
<WrappedComponent
{...this.props}
error={this.state.error}
checkFieldsError={this.checkFieldsError}
>
{children}
</WrappedComponent>
);
}
}
return HOC;
};
export default fieldHOC;
Component in which I'm using this HOC is
const FieldValidation = fieldHOC(View);
class Account extends Component {
//Some class functions
render() {
const { spaceBottom, error } = this.state;
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
keyboardShouldPersistTaps="handled"
behavior={Platform.OS === 'ios' ? 'padding' : null}
>
<KeyboardAwareScrollView
keyboardShouldPersistTaps="handled"
alwaysBounceVertical={false}
contentInset={{ bottom: 0 }}
>
<FieldValidation
name_first={this.state.name_first}
name_last={this.state.name_last}
email={this.state.email}
{...this.props}
>
<View
style={[
styles.UserContainer,
CommonStyle.shadowStyle
]}
>
<Text style={styles.headingTextStyle}>
Account Details
</Text>
<FormTextInputComponent
{...testID('first_name')}
errorType={this.props.error.name_first.errorType}
onChangeText={this.handleTextChange('name_first')}
textInputRef={ref => {this.name_first = ref;}}
autoCapitalize="none"
spellCheck={false}
autoCorrect={false}
blurOnSubmit={false}
onSubmitEditing={() => {
this.props.checkFieldsError('name_first', 'name_last');
}}
/>
{this.props.error.name_first.errorType ?
(
<ErrorMessage textColor="#EA2027" error={error.name_first} />
)
: null}
//Last part
export default fieldHOC(connect(mapStateToProps)(Account));
In the above component, I'm trying to call the validation function written in HOC which is checkFieldsError.
The problem which I'm facing is that the props passed in <FieldValidation like name_first are accessible in HOC's render function but same props are not accessible in the class functions of HOC.
Most probably what I tried to do is an antipattern in React(my guess). Can someone can please help me in figuring out the problem and the proper way to do it?
Edit: Sample implemented in codesandbox Example
Here is a codeSandBox example I have created with what you are trying to achieve, you will notice that inside the HOC I try to access the props in its functions, also check the console to see the console logs, please check the code and follow the same example. You will achieve the same results.
I have created a form which consists of two drop down list fields (amongst other form elements), using React Native Modal Dropdown plugin. The two dropdowns are:
Country List
States list (filtered by the what country that was selected above)
I am currently having a problem with the States Dropdown, which recieves a countryId value from state that is set when a item is selected from the the Country Drop Down. This is then used to pass to my State Drop Down.
It's worth noting that the two dropdown's I have listed above have been seperated into components for reusability.
Code for Form:
static propTypes = {
navigation: PropTypes.object
};
state = {
countryId: 0,
stateId: 0
}
componentWillMount() {
}
onCountryDropDownSelected = (val) => {
this.setState({ countryId: val });
}
onStateDropDownSelected = (val) => {
this.setState({ stateId: val });
}
render() {
return (
<Container>
<StatusBar barStyle="default" />
<CountryDropDownList onSelect={this.onCountryDropDownSelected} />
<Text>Country: {this.state.countryId}</Text>
<StateDropDownList onSelect={this.onStateDropDownSelected} countryId={this.state.countryId} />
</Container>
);
}
Code for StateDropDownList component:
class StateDropDownList extends Component {
static propTypes = {
countryId: PropTypes.number,
onSelect: PropTypes.func
};
state = {
data: [],
errorMessage: '',
isLoading: false
}
componentWillReceiveProps(nextProps) {
if (nextProps.countryId != undefined) {
return this.populatePicker(nextProps.countryId);
}
}
populatePicker(countryId) {
// This method fetches data from the API to store in the 'data' state object.
}
dropDownRenderRow(rowData, rowID, highlighted) {
let evenRow = rowID % 2;
return (
<TouchableHighlight underlayColor='cornflowerblue'>
<View style={{backgroundColor: evenRow ? 'lemonchiffon' : 'white'}}>
<Text style={{color: 'black'}}>
{rowData.name}
</Text>
</View>
</TouchableHighlight>
);
}
dropDownRenderButtonText(rowData) {
console.log('dropDownRenderButtonText', rowData);
return rowData.name;
}
dropDownRenderSeparator(sectionID, rowID, adjacentRowHighlighted) {
return (<View key={rowID} />);
}
dropDownOnSelect(rowID, rowData) {
// Get the selected value and pass to parent component through onSelect() event in parent.
this.props.onSelect(rowData.id);
}
render() {
return (
<View>
<Text>Home State</Text>
{this.state.data && this.state.data.length > 0 ?
<ModalDropdown defaultValue='Select one'
style={{flex:1}, {marginTop: 10}}
options={this.state.data}
onSelect={(rowID, rowData) => this.dropDownOnSelect(rowID, rowData)}
renderButtonText={(rowData) => this.dropDownRenderButtonText(rowData)}
renderRow={this.dropDownRenderRow.bind(this)}
renderSeparator={(sectionID, rowID, adjacentRowHighlighted) => this.dropDownRenderSeparator(sectionID, rowID, adjacentRowHighlighted)} />
:
<Text>No regions for country.</Text>
}
</View>
);
}
}
What I am noticing is that the 'componentWillReceiveProps' function is stopping my dropdown from having a selected value. But unfortunately, I need this function in order for the props to update when passing in the countryId value from the parent.
When I remove the this.props.onSelect(rowData.id); line in this dropDownOnSelect() function, the dropdown value gets set correctly. I guess this is the case, since I am not setting an prop value.
I can see what the issue is, but not a way to get around it.
Any help is appreciated!
I resolved the problem. Made a simple error to check if the nextProps.countryId is not the same as props.countryId, which now makes sense:
componentWillReceiveProps(nextProps) {
if (nextProps.countryId && nextProps.countryId != this.props.countryId) {
return this.populatePicker(nextProps.countryId);
}
}
I want to generate a sliders dynamically according to user input, and don't know how to save values on change. Following is the code given of my implementation.
The problem is that I can't get value via event.target.value
// priceCities is an array of objects:
handlePrices(priceCities){
return priceCities.map( (cstate, index) => (
<li key={index} >{cstate.name} <Slider key={index} min={3} max={500} step={1} style={{height: 100}} axis="y"
defaultValue={5} id ={cstate.id} onChange={ this.handleSlider.bind(this,cstate.id )} value={this.state.values[cstate.id] } /> <span>{this.state.values[cstate.id]}</span> </li>
));
}
this.state = {
values: []
}
and onChange() method here:
handleSlider ( event,i ) {
// this.state.sliderValue[event.target.id] = event.target.value;
//console.log('handlerslider'+event.target.id+' '+event.target.value);
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
}
Finally I found solution by defining the onChange method like this :
onChange={(event,value) => this.handleSlider(event, value,currState.id )}
and the code of handleSlider function :
handleSlider (event, value,id) {
let values = [...this.state.sliderValue];
values[id] = value;
this.setState({sliderValue: values });
console.log('handlerslider'+value+' '+id);
}
2020 Answer with Hooks
Something simple like this will work:
<Slider
onChange={(_, value) =>
setState(value)
}
step={1}
min={1}
max={50}
value={value}
valueLabelDisplay="auto"
/>