this.state.array.map is not a function error - javascript

I have declared an array of objects in the state that represent TextInput fields. Now I want to add value onChangeText of my inputs but as soon as I try to write something on the input I get the error: 'this.state.sede_array is not a function'. I did console.log of the array and I see that only the first letter of my input is added to the value property and then the error fires up. Please help to figure out what is happening.
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
sede_array:[
{id:1, value:null},
{id:2, value:null},
{id:3, value:null},
]
};
}
render() {
console.log(this.state.sede_array)
let sede_list= this.state.sede_array.map((val, key) => {
return(
<View key={key}>
<Text style={styles.titolo}>Indirizzo Sede
{this.state.sede_array[key].id}</Text>
<TextInput style={styles.input}
onChangeText={value => {
sede_array[key].value = value;
this.setState({sede_array})
}}
/>
</View>
)});

I have a feeling you are clobbering your array. Inside the onChangeText event handler, you are setting state; but do you know what the value is? You use sede_array without (this.state) as a prefix. I'm assuming sede_array variable doesn't exist globally, thus it would assign 'undefined' to this.state.sede_array when you call setState(). Since setState will trigger another render, on that render cycle it would crash because this.state.sede_array would be undefined. You can fix this by adding this.state. infront of sede_array inside onChangeText.

Related

cannot update a component from inside the function body of a different component - React Native?

I have a child component "Text Input" and passes the value to as a prop like this
export default function MobileInput(props) {
const [mobileNumber, setMobileNumber] = React.useState('');
return (
<View style={styles.inputBox}>
<TextInput
value={mobileNumber}
onChangeText={(number) => setMobileNumber(number)}
onEndEditing={props.saveMobileNumber(mobileNumber)} // here
/>
</View>
);
}
In Parent, I got the value from child
const [mobile, setMobile] = useState('');
const getMobile = (number) => {
number ? setMobile(number) : null; // here's I got this warnning
console.log('getMobile-number-from-child', number);
};
const reSendMobile = () => { // other function I want to call passed on mobile number I got from child component
if (mobile?.length) {
alert('Done');
setReSend(false);
setSeconds(10);
} else {
alert('Please write your number before press send!');
}
};
<MobileInput saveMobileNumber={getMobile} />
I see this issue But I'm already using React 16.13.1
TextInputs property onEndEditing accepts a function that is called when text input ends.. Instead of a function, you are passing the result of your props.saveMobileNumber function that is called when the component renders. Try passing a function that calls saveMobileNumber instead:
onEndEditing={() => props.saveMobileNumber(mobileNumber)}
Your code will be much easier to read/debug if you avoid keeping the same state in multiple components. You can pass mobile and setMobile to the child through props and avoid having to create separate state for the same data.
Try this:
<View style={styles.inputBox}>
<TextInput
value={mobileNumber}
onChangeText={(number) => setMobileNumber(number)}
onEndEditing={() => props.saveMobileNumber(mobileNumber)} // change here
/>
</View>
The event onEndEditing accepts a function call
Just update to call a arrow function :
onEndEditing={() => props.saveMobileNumber(mobileNumber)}
For me, i was updating activity title outside the useEffect hook. When i moved the code
into useEffect hook, the error just gone.

How do I prevent a function from being run before data is loaded into this.state (i.e. in componentDidMount)?

I have a function getRepresentativeName() which uses a piece of data (this.state.communityData.Representative) from this.state to index into a database and retrieve a string. My intent is that when the component mounts, all of the information in this.state, including this.state.communityData.Representative, will be populated and the representative's name will appear in the location set off by {...}. However, the function runs immediately when the first render is called and does not wait for the component to mount; as a result, since this.state is not populated yet, the getRepresentativeName function crashes.
I placed a ternary statement to check whether the communityData JSON object has been filled (the logic being that if it is not filled, i.e. === {}, just return null instead of executing getRepresenativeName()); Interestingly enough, the page crashes regardless (with the same issue: getRepresenativeName() is being called before this.state is populated in componentDidMount()).
Can someone explain to me why this is happening, and how I could resolve this situation without adding redundant data to this.state? I'm not sure if this is the way to solve it, but I thought that the problem could be solved if there was a way to make the getRepresentativeName() function wait until componentDidMount() finishes. Thank you!
Note: If I do not call the function and just replace the {...} with {this.state.communityData.Representative}, which fills the element with the Representative ID as opposed to the name, the page renders perfectly. In addition, I tried making getRepresentative name a callback, and that still didn't work (i.e. the page still crashed when it tried to render).
async getRepresentativeName() {
const representativeSnapshot = await database()
.ref('users')
.child(this.state.communityData.Representative)
.child('name')
.once('value');
return representativeSnapshot.val();
};
render() {
console.log('starting render');
return (
<View style={styles.containerScreen} testID="communityScreen">
{/*
This the container that holds the community Image
and its description,
*/}
<View style={styles.containerhorizontal}>
<View style={styles.containervertical}>
<Image
source={require('../../sample_images/va10.jpg')}
style={styles.image}
/>
</View>
<View style={styles.containervertical}>
<Text style={styles.containerhorizontal}>
Description: {this.state.communityData.Description}
</Text>
<Text style={styles.containerhorizontal}>
Representative: **{this.state.communityData !== {} ?this.getRepresentativeName() : null}**
</Text>
...
The typical pattern here is initialize your state with some kind of loading property. Then, asynchronously load data based on input from the user interface. That input could be from the user or it could be from lifecycle events. In this case, componentDidMount. Your component should know how to render when loading is true and when data is available.
class MyComponent extends React.Component {
state = {loading: true, error: null, representative: null}
componentDidMount() {
fetchData().then((representative) => {
this.setState({ loading: false, representative })
}).catch((error) => {
this.setState({ loading: false, error: error.message })
})
}
render() {
return this.state.error
? <span>woops</span>
: this.state.loading
? <span>busy</span>
: <div>Representative: {this.state.representative.name}</div>
}
}

How to send a ref to a function as a parameter in React Native?

I've imported a custom component into my screen and rendered it in the render() function. Then, created a ref to that custom component. Now, the render() function simply looks like this.
render() {
return (
<View>
<MyComponent ref={component => this.myComponent = component} />
</View>
)
}
Then, I've created another function to access the state of my custom component. I wrote it like this.
myFunction = (ref) => {
ref.setState({ myState: myValue })
}
Then, I called that function like this.
this.myFunction(this.myComponent)
But, it does not work. It gives me the following error.
null is not an object (evaluating 'ref.setState')
Actually what I need this myFunction to do is,
this.myComponent.setState({ myState: myValue })
Can you please help me to solve this problem?
ref is not your this object. it's dom for your componnet. For setState you need this of your component.
you can pass this as argument.
myFunction(this)
Now you will be able to do ref.setState in myFunction.
function myFunction(ref) {
ref.setState({ myState: myValue })
}
To use setState, just use your component's context (this keyword). The context also have your ref in it, so you don't need to pass it as an argument if you are inside one component(not forwarding down to children)
myFunction = (event) => {
this.myComponent // -> points to your ref, DOM element
this.setState() // use your setState like that
}
Don't forget to bind your context in parent component if you want to pass the handler to the child components. Refer to this useful topic
EDIT: Based on your comment, I guess you want to update the parent state by calling a handler in some other component. To do that, you need to create a handler in your parent component, bind the context and pass it as a property to the child component. Next up, you need to assign this handler in your child component. You cannot pass a context with setState method via argument or ref, this is just not how it works in javascript and in react.
Example:
// ParentComponent.js
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 1,
};
this.onChangeHandler = this.onChangeHandler.bind(this);
}
onChangeHandler(event) {
this.setState({
value: someNewValue // will update state on parent component
})
}
render() {
return (
<View>
<SomeComponent>{this.state.value}</SomeComponent>
<ChildrenComponent onChangeHandler={this.onChangeHandler} />
</View>
);
}
}
// ChildrenComponent.js
const ChildrenComponent = (props) => (
<View>
<Button
onPress={props.onChangeHandler}
title="click me to change parent state"
/>
</View>
);
Hopefully, this is what you need :)

React setState with dynamic key and object value not working as expected

i'm facing an issue with react's method (setState), hope you can help.
I have a handleChange method using dynamic keys to 'persist' data in the state.. i looks like this:
handleChange = (event, group) => {
event.persist(); //Used to avoid event recycling.
this.setState(
prevState => ({
...prevState,
[group]: { ...prevState[group], [event.target.name]: event.target.value }
}),
() => console.log("state", this.state)
);
};
this method works pretty well when theres just one 'instance' of my custom component using the mentioned handleChange method. The problem began when i wanted to have several components using that method, because when called, its overriding the prevState value. For example:
Initial state: {mockedValue:'Im here to stay'}
then i call handleChange for group 'alpha', to add to this values {name:a},
Next state: {alpha:{name:a},mockedValue:'Im here to stay'}
then i call handleChange for group 'beta', to add to this values {otherName:b},
expected state: {alpha:{name:a}, beta:{otherName:b},mockedValue:'Im here to stay'}
Next state : beta:{otherName:b},mockedValue:'Im here to stay'}
Not sure why this is happening, perhaps i'm misunderstanding some concept, the fact is that i don't have idea why this is not working as expect, (perhaps it's because computed name value, but not sure..) Do you have any idea how to solve this?
Thanks for reading! :)
Update
Code in sandbox: https://codesandbox.io/s/v3ok1jx175
Update2: SOLVED
Thanks for your support Thollen and DevSerkan, i really appreciate it.
The problem was that i had the handleChange event at the wrong level... it means that i was defining the handleChange method inside the child Componet, for instance:
class Parent extends React.Component {
render(){
return(
<div>
<Child/>
<Child/>
<Child/>
</div>
);
}
}
so there was just one 'instance' of handleChange method shared by all the 'instances' , it's a wrong approach. to solve this, i modified Parent like this:
class Parent extends React.Component {
handleChange(){
//updateState Operations here...
}
render(){
return(
<div>
<Child handleChange = {this.handleChange}/>
<Child handleChange = {this.handleChange}/>
<Child handleChange = {this.handleChange}/>
</div>
);
}
}
in this way, i removed the responsibility of handling change from the 'top level child' to the parent, where the child components were being used.

Insert the pressed item into an array in react-native

I have a list of tag objects in array (tagList), which I am fetching from the server and storing into the state. After a succesfull fetch I am mapping each item inside a ScrollView.
export default class RegisterTags extends Component {
constructor(props) {
super(props)
this.state = {
tagList: [],
selectedTags: [],
}
}
componentWillMount() {
api.getTagsOnRegister()
.then((res) => {
this.setState({
tagList: res
})
})
}
insertTag = (tag) =>{
this.setState({
selectedTags: this.state.selectedTags.push(tag)
})
}
render() {
return(
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.ScrollViewContainer}>
{this.state.tagList.map((tag) => {
return(
<View style={styles.tagStyle} key={tag.id} onPress={this.insertTag(tag)}>
<Text style={styles.tagText}>{tag.name}</Text>
</View>
)
})}
</ScrollView>
</View>
)
}
}
What I want to achieve is, when I press on any of the tag, I would like to add that object into the selectedTags array. But I am getting error:
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
Possible Unhandled Promise Rejection (id: 0):
_this.state.selectedTags.push is not a function
TypeError: _this.state.selectedTags.push is not a function
How can I add the pressed tag item into the selectedTags array?
onPress={this.insertTag(tag)}
Issue: in your render function you are directly calling a function that changes state. Render function should never call any function that updates state.
So how to call onPress?
Ans: as written below
onPress = {() => {this.insertTag(tag) }}
Will code work now?
Answer is no.
You have to put your views inside TouchableHighlight and move onPress method from View to TouchableHighlight.
Then hopefully your code works. I am assuming everything is setup property.
~~~~~~EDIT 1~~~~~~~~
What't the difference between onPress={this.insertTag(tag)} and onPress = {() => {this.insertTag(tag) }}
Every thing inside curly braces are expressions in react jsx. Now onPress={this.insertTag(tag)} evaluates the expression inside curly braces and assign it to onPress property, and in your case this.insertTag happens to update state.
While onPress = {() => {this.insertTag(tag) }} , on evaluating curly braces returns a function, but doesn't call that function. And when onPress event is triggered than that function is called.
Google "Arrow Function" for more.

Categories

Resources