RN - Map directly from state syntax - javascript

I'm trying to map an array from state - but confused re the correct syntax - can anyone please advise where i'm going wrong:
This is what I have at the mo:
newsStorys = () => {
return (
{this.state.newsFeed.map((a) => {
<View style={ModalStyles.newsArticle}>
<Text style={ModalStyles.newsDate}>{a.date}</Text>
<Text style={ModalStyles.newsTitle}>{a.title}</Text>
<Text style={ModalStyles.newsDesc}>
{a.story}
</Text>
</View>
}
}
);
};

I'm not sure if that is the whole code of your component, but I can see three things.
If newsFeed is not initialized when component first render (let's say it is undefined yet), then newsFeed.map()will throw an exception.
You are not returning anything from map call. you should write something like this:
newsStorys = () => {
if (!this.state.newsFeed) return null;
return this.state.newsFeed.map((a) => ({ // <--- note the parentheses here, you don't have it
<View style={ModalStyles.newsArticle}>
<Text style={ModalStyles.newsDate}>{a.date}</Text>
<Text style={ModalStyles.newsTitle}>{a.title}</Text>
<Text style={ModalStyles.newsDesc}>
{a.story}
</Text>
</View>
});
);
};
If you want to avoid the parentheses, then you need to explicitly return something, like this:
this.state.newsFeed.map((a) => {
return (
<View style={ModalStyles.newsArticle}>
<Text style={ModalStyles.newsDate}>{a.date}</Text>
<Text style={ModalStyles.newsTitle}>{a.title}</Text>
<Text style={ModalStyles.newsDesc}>
{a.story}
</Text>
</View>
);
});
It is possible that you need an extra view to wrap the list of views returned by map.
Also you need to provide a unique key to each view, so React can keep track on them.
<View style={ModalStyles.newsArticle} key={'nome unique value'}>
...
</View>
Finally I think it would be better using a FlatList instead of map.
Cheers!

Had a play and a good dig around the web and found the syntax answer: (Thanks to Bruno for the Key and pointers).
newsStorys = () => {
return this.state.newsFeed.map((value, index) => {
return (
<View style={ModalStyles.newsArticle} key={index}>
<Text style={ModalStyles.newsDate}>{value.date}</Text>
<Text style={ModalStyles.newsTitle}>{value.title}</Text>
<Text style={ModalStyles.newsDesc}>{value.story}</Text>
</View>
);
});
};

Try this
newsStorys = () => (
this.state.newsFeed.map(({ date, story, title }, index) =>
<View key={`news-${index}`} style={ModalStyles.newsArticle}>
<Text style={ModalStyles.newsDate}>{date}</Text>
<Text style={ModalStyles.newsTitle}>{title}</Text>
<Text style={ModalStyles.newsDesc}>{story}</Text>
</View>
));

Related

How to solve infinite loop in JSX?

Im trying to map my userForm state to render each by each , but the map function sometimes returns me as undefiend and sometimes as an react infinite loop and I'm not finding a way to solve it, somene could help me ?
my code:
const FormScreen = async({route}) => {
const [userForm, setuserForm] = useState([]);
if (userForm.length > 0) {
console.log(userForm,'campos:',userForm.fields);
return;
} else {
setuserForm(await JSON.parse(route.params.paramKey));
}
{...}
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<Text style={styles.textStyle}>
COLLECTION :
</Text>
{userForm.map((item) => (
<Text keys={item.fields}>test</Text>
))}
</View>
</SafeAreaView>
);
};
Move setuserForm(await JSON.parse(route.params.paramKey)); into a useEffect hook.
https://reactjs.org/docs/hooks-effect.html

Delete the actual Object in a map - Javascript

Basically i'm trying to work with a map in javascript. And when i'm using a button, i want to delete the actual object used by the map (not only a element, literally all the things on this part of map). And when it's done, i'm reading the next etc...
Right now i have a map, i'm reading all my objects, but i don't know how can i delete the actual Object Used.
Show() {
return this.state.loads.map((data) => {
return (
<View style={{ flexDirection: 'row' }}>
<Text key={data} style={styles.inputText}>{data.service}</Text>
<TouchableOpacity style={styles.buttonDel} onPress={() => this.delete(data.id)}>
<Icon reverse name='delete' type='material' color='#54a9eb' size='20'/>
</TouchableOpacity>
</View>
)
})
}
If anyone have an idea, thanks.
splice in javascript removes your target element ... but you need to create a new copy of your loads array (in a new location in memory) ... cause react does only shallow comparison to determine when to re-render
deleteItem = (index) => {
const { loads } = this.state;
/** Important to make `setState` force refresh */
let newLoads = loads.slice();
newLoads = newLoads.splice(index, 1); /* Remove one element at index*/
this.setState({ loads: newLoads });
};
return this.state.loads.map((data, index) => {
return (
<View style={{ flexDirection: 'row' }}>
<Text key={data} style={styles.inputText}>{data.service}</Text>
<TouchableOpacity style={styles.buttonDel} onPress={() => this.deleteItem(indx)}>
<Icon reverse name='delete' type='material' color='#54a9eb' size='20'/>
</TouchableOpacity>
</View>
)

undefined is not a function (near '...this.state.results.map...')

I'm new at react native.I have a problem that i've been dealing for a few days.I want to show my data with map.Can you guys show me where i did wrong?
(I think i made mistake in the results inside scrollview)
class Movie extends Component{
state= {
apiurl:'http://www.omdbapi.com/.............................',
s:'',
results: [],
selected:{}
}
searchFunc(s) {
this.setState({s: s})
axios(this.state.apiurl+ "&s="+s).then(response =>
this.setState({results: response.data.Search[0]}));
console.log(this.state.results)
}
render() {
return(
<View style={{flex:1,backgroundColor:'#356292'}}>
<View
style={styles.sectionContainer}>
<View style={styles.section}>
<TextInput style={styles.section2}
onChangeText = {(s) => this.searchFunc({s})}
value={this.state.s}
placeholder="Movies,Series.."
>
</TextInput>
<TouchableOpacity
style={{justifyContent: 'center', alignItems: 'center'}}
onPress={() => this.searchFunc()}>
<Image
source ={require('../img/seach.png')}
style={{width:width*0.05,height:height*0.03}}
>
</Image>
</TouchableOpacity>
</View>
</View>
<ScrollView style={styles.scroll}>
{this.state.results.map(results=>(
<View key={this.state.results.imdbID}
style={styles.scroll2}>
<Image source={{uri: this.state.results}}
style={{width:width*0.3, height:height*0.4}}>
</Image>
<Text style={styles.heading}>
{this.state.results.Title}
</Text>
</View>
))}
</ScrollView>
</View>
);
}
From the comments from your original post, i'm guessing response.data.Search[0] is also an array. If so your results state update is okay. Otherwise you should set the results state to response.data.Search.
Other problem I noticed in your code is in the map function, you are mapping the array with each item refering to the result variable, but in your jsx, you are trying to access the results state variable. It should be
<ScrollView style={styles.scroll}>
{
this.state.results.map(result => (
<View
key={result.imdbID}
style={styles.scroll2}
>
<Image
source={{uri: result.image}} // use correct key from result object
style={{width:width*0.3, height:height*0.4}}
/>
<Text style={styles.heading}>{result.Title}</Text>
</View>
))
}
</ScrollView>
Also in your Textinput onChangeText callback function, you dont have to pass that value as an object. You can change it to
<TextInput
style={styles.section2}
onChangeText={(s) => this.searchFunc(s)}
value={this.state.s}
placeholder="Movies,Series.."
/>
I quess you are supposed to pass Search to the state and not the Search[0], which is the object and not map, the reason map function is not working on it.
axios(this.state.apiurl+ "&s="+s).then(response =>
this.setState({results: response.data.Search}));
console.log(this.state.results)
Coincidently, I have created an app using same movie API, you can find it here: Github Repo, Movie App

set image in returned map() object

I cannot load an image after calling map function:
this.rosImgs.push({img: '../imgs/ros_eat.png', id: this.imgId});
getRosette(){
return this.rosImgs.map((obj) =>
<TouchableHighlight key={obj.id} onPress={() => this.imgClick()}>
<Image
source={require(obj.img)}
/>
</TouchableHighlight>
);
}
The following error occures:
Unknown named module:'../imgs/ros_eat.png'
but when i do source={require('../imgs/ros_eat.png')}
it works.
Please help
In react native the images cannot be loaded dynamically and are parsed before. So I suggest changing the code like below.
this.rosImgs.push({img: require('../imgs/ros_eat.png'), id: this.imgId});
getRosette(){
return this.rosImgs.map((obj) =>
<TouchableHighlight key={obj.id} onPress={() => this.imgClick()}>
<Image
source={obj.img}
/>
</TouchableHighlight>
);
}

Undefined is not an object evaluating this.state/this.props

How do I bind a function outside of scope in React Native? I'm getting the errors:
undefined is not an object evaluating this.state
&
undefined is not an object evaluating this.props
I'm using the render method to evoke renderGPSDataFromServer() when the data has been loaded. The problem is, I'm trying to use _buttonPress() and calcRow() inside of renderGPSDataFromServer(), but I'm getting those errors.
I've added
constructor(props) {
super(props);
this._buttonPress = this._buttonPress.bind(this);
this.calcRow = this.calcRow.bind(this);
to my constructor and I've changed _buttonPress() { to _buttonPress = () => { and still nothing.
I think I understand the problem but I don't know how to fix it:
renderLoadingView() {
return (
<View style={[styles.cardContainer, styles.loading]}>
<Text style={styles.restData}>
Loading ...
</Text>
</View>
)
}
_buttonPress = () => {
this.props.navigator.push({
id: 'Main'
})
}
renderGPSDataFromServer =() => {
const {loaded} = this.state;
const {state} = this.state;
return this.state.dataArr.map(function(data, i){
return(
<View style={[styles.cardContainer, styles.modularBorder, styles.basePadding]} key={i}>
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress().bind(this)}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
<View style={styles.cardContentRight}>
<Text style={styles.restData}>{i}</Text>
<View style={styles.gpsDataContainer}>
<Text style={styles.gpsData}>
{Number(data.lat).toFixed(2)}</Text>
<Text style={styles.gpsData}>{Number(data.long).toFixed(2)}</Text>
</View>
<Text style={styles.gpsData}>
{this.calcRow(55,55).bind(this)}
</Text>
</View>
</View>
);
});
}
render = ()=> {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return(
<View>
{this.renderGPSDataFromServer()}
</View>
)
}};
How do I go about fixing this and in this case what is the problem?
this.props are read-only
React docs - component and props
And therefore a component shouldn't try a to modify them let alone mutate them as you are doing here:
_buttonPress = () => {
this.props.navigator.push({
id: 'Main'
})
}
I'd suggest using state instead:
_buttonPress = () => {
this.setState = {
...this.state,
navigator: {
...this.state.navigator,
id: 'Main'
}
}
}
Regarding your binding issue:
the .map method takes a 2nd argument that is used to set the value of this when the callback is invoked.
In the context of your question, you just need to pass thisas the 2nd argument to you .map method to bind the components scope's this to it.
This is happening because, the function inside the map method creates a different context. You can use arrow functions as the callback in the map method for lexical binding. That should solve the issue you are having.
renderGPSDataFromServer =() => {
const {loaded} = this.state;
const {state} = this.state;
return this.state.dataArr.map((data, i) => {
return(
<View style={[styles.cardContainer, styles.modularBorder, styles.basePadding]} key={i}>
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress().bind(this)}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
<View style={styles.cardContentRight}>
<Text style={styles.restData}>{i}</Text>
<View style={styles.gpsDataContainer}>
<Text style={styles.gpsData}>
{Number(data.lat).toFixed(2)}</Text>
<Text style={styles.gpsData}>{Number(data.long).toFixed(2)}</Text>
</View>
<Text style={styles.gpsData}>
{this.calcRow(55,55).bind(this)}
</Text>
</View>
</View>
);
});
}
Also, once you've used arrow functions in the class function definition you
don't need to bind them in constructor like:
constructor(props) {
super(props);
this._customMethodDefinedUsingFatArrow = this._customMethodDefinedUsingFatArrow.bind(this)
}
Also, once you have defined class functions as arrow functions, you
don't need to use the arrow functions while calling them either:
class Example extends React.Component {
myfunc = () => {
this.nextFunc()
}
nextFunc = () => {
console.log('hello hello')
}
render() {
// this will give you the desired result
return (
<TouchableOpacity onPress={this.myFunc} />
)
// you don't need to do this
return (
<TouchableOpacity onPress={() => this.myFunc()} />
)
}
}
not sure if this is the problem, but I think is code is wrong, and may be potentially causing your issue.
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress().bind(this)}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
specifically this line onPress={this._buttonPress().bind(this)}>
you are invoking the function and binding it at the same time.
The correct way to do this would be so
onPress={this._buttonPress.bind(this)}>
this way the function will be called only onPress.
You are going in the right direction, but there is still a minor issue. You are passing a function to your map callback that has a different scope (this) than your component (because it is not an arrow function), so when you do bind(this), you are rebinding your callback to use the scope from map. I think this should work, it basically turns the callback that you pass to map into an arrow function. Also, since you bind your function in the constructor, you do not need to do it again:
// The constructor logic remains the same
// ....
renderLoadingView() {
return (
<View style={[styles.cardContainer, styles.loading]}>
<Text style={styles.restData}>
Loading ...
</Text>
</View>
)
}
_buttonPress = () => {
this.props.navigator.push({
id: 'Main'
})
}
renderGPSDataFromServer =() => {
const {loaded} = this.state;
const {state} = this.state;
return this.state.dataArr.map((data, i) => {
return(
<View style={[styles.cardContainer, styles.modularBorder, styles.basePadding]} key={i}>
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
<View style={styles.cardContentRight}>
<Text style={styles.restData}>{i}</Text>
<View style={styles.gpsDataContainer}>
<Text style={styles.gpsData}>
{Number(data.lat).toFixed(2)}</Text>
<Text style={styles.gpsData}>{Number(data.long).toFixed(2)}</Text>
</View>
<Text style={styles.gpsData}>
{this.calcRow(55,55).bind(this)}
</Text>
</View>
</View>
);
});
}
render = ()=> {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return(
<View>
{this.renderGPSDataFromServer()}
</View>
)
}};

Categories