React Native Rendering Components Alternatively by using state - javascript

I want to render the list alternatively into two columns in a grid. I intend to do so using a flag variable in my component state and change its value each time I return a CardDetail.
I commented out the this.changeState() statements because they don't seem to work as intended.
Please help me figure this is out, I am new to React/React Native.
Thanks in advance.
renderAlbums() {
return this.state.albums.map(album =>{
this.rend2(album)
}
);
}
rend2(album){
if(this.state.flag === 0)
{
//this.changeState();
return (<CardDetail key={album.title} album={album} />);
}
else
{
//this.changeState;
return (<View />);
}
}
changeState(){
if(this.state.flag === 0)
{
this.setState({flag:1});
}
else
{
this.setState({flag:0})
}
}
render() {
console.log(this.state);
return (
<ScrollView>
<Grid>
<Col>{this.renderAlbums()}</Col>
<Col>{this.renderAlbums()}</Col>
</Grid>
</ScrollView>
);
}
}

I don't think you need to change the state in this case. Just pass a parameter to your renderAlbums() method and use it to pick every other album in your list:
renderAlbums(col) {
return this.state.albums.map( (album, index) => {
if (index % 2 == col)
return (<CardDetail key={album.title} album={album} />);
else
return (<View/>);
} );
}
Then in your render() method:
render() {
return (
<ScrollView>
<Grid>
<Col>{this.renderAlbums(0)}</Col>
<Col>{this.renderAlbums(1)}</Col>
</Grid>
</ScrollView>
);
}
This code has not been tested, but it should work.

Maybe change your approach a little bit and render both columns at the same time. So then you can switch between which column you want to render CardDetail.
return (
<ScrollView>
<Grid>
{this.state.albums.map((album, index) =>
<View key={index}>
<Col>{index % 2 === 0
? <CardDetail key={album.title} album={album} />
: <View />
}</Col>
<Col>{index % 2 !== 0
? <CardDetail key={album.title} album={album} />
: <View />
}</Col>
</View>
}
</Grid>
</ScrollView>

Related

Display only one element at a time in react with .map()

So basically I'm creating a quiz component, and I need to loop through all the questions, but display only one question at a time, and after the question is answered, display another one.
How could I do that? I would normally use for loop, but it is not recommended, how can I achieve the same outcome with .map(), or any other function?
This is basically what I want to happen:
for(let i = 0; i < quiz.questions.length(); i++) {
return (
<Content>
<View style={styles.gutter}>
<View style={styles.container}>
<View style={styles.content}>
<Text>{quiz.questions[i].question}</Text>
</View>
<Button
primary
label={quiz.answers.option1}
onPress={() => {
quiz.answers.option1 === quiz.questions[i].rightAnswer
? continue // would continue work here as expected?
: console.log("Wrong");
}}></Button>
<Button
primary
label={quiz.answers.option2}
onPress={() => {
quiz.answers.option2 === quiz.questions[i].rightAnswer
? continue // would continue work here as expected?
: console.log("Wrong");
}}></Button>
<Button
primary
label={quiz.answers.option3}
onPress={() => {
quiz.answers.option3 === quiz.questions[i].rightAnswer
? continue // would continue work here as expected?
: console.log("Wrong");
}}></Button>
</View>
</View>
</Content>
);
}
You should create a functional component with useState hook:
import React, { useState } from 'react';
function Quiz() {
const [index, setIndex] = useState(1);
const current = quiz.questions[index];
return (
<Content>
<View style={styles.gutter}>
<View style={styles.container}>
<View style={styles.content}>
<Text>{current.question}</Text>
</View>
<Button
primary
label={quiz.answers.option1}
onPress={() => {
if (quiz.answers.option1 === current.rightAnswer) {
setIndex(index + 1);
} else {
console.log('Wrong');
}
}}
/>
<Button
primary
label={quiz.answers.option2}
onPress={() => {
if (quiz.answers.option2 === current.rightAnswer) {
setIndex(index + 1);
} else {
console.log('Wrong');
}
}}
/>
<Button
primary
label={quiz.answers.option3}
onPress={() => {
if (quiz.answers.option3 === current.rightAnswer) {
setIndex(index + 1);
} else {
console.log('Wrong');
}
}}
/>
</View>
</View>
</Content>
);
}
If you want to show only one answer you should write a code like this:
I think you wanted to load one item already and you got undefined error message, so that's why you are asking your question
Let's assume you are getting your data via props of redux(it can be anything like regular state)
Render(){
<div>
{this.props.mydata &&
this.props.mydata.quiz[0] &&
this.props.mydata.quiz[0].question
}
</div>
}
This code checks your data, whenever the data loaded you can use it and you won't get any error. And you need to make it whenever your player answered right it goes +1 in array.
I hope you got the idea and works for you

React Native: Access this.props Inside Function

I managed to fetch data and show to UI with this code:
export default class BoxGarage extends Component {
render() {
let garage = this.props.garage;
garage.name = garage.name.replace('strtoreplace', 'My Garage');
let cars = garage.cars.length ?
garage.cars.map((val, key) => {
return (
<Car key={key} car={val} />
)
}) : (
<View style={styles.boxEmpty}>
<Text style={styles.textEmpty}>(No Cars)</Text>
</View>
);
return (
<View style={styles.boxGarage}>
<Text>{ garage.name }</Text>
{ cars }
</View>
)
}
}
Then I tried to change with a function, but no cars shown. What is missing?
export default class BoxGarage extends Component {
render() {
let garage = this.props.garage;
garage.name = garage.name.replace('strtoreplace', 'My Garage');
cars = function(garage) {
if (garage.cars.length) {
garage.cars.map((val, key) => {
return (
<Car key={key} car={val} />
);
});
}
else {
return (
<View style={styles.boxEmpty}>
<Text style={styles.textEmpty}>(No Cars)</Text>
</View>
);
}
}
return (
<View style={styles.boxGarage}>
<Text>{ garage.name }</Text>
{ cars(this.props.garage) }
</View>
)
}
}
And I think I should refactor for best practice either using constructor or just move the function outside render, but I don't know what it is. Please advice.
The reason your second code doesn't work is that you're not returning anything from your function if garage.cars.length > 0.
if (garage.cars.length) {
// Added a return statement on the next line
return garage.cars.map((val, key) => {
return (
<Car key={key} car={val} />
);
});
}
That said, i think your first version of the code was much cleaner. If a piece of code got complicated enough that i was tempted to make an inline function to do calculations, i'd either pull that out to another class method, or to another component. In your case though, just doing a ternary or an if/else will be much better.

React native render more dimensional array

I would like to map a more dimensional array that looks like this:
const array = [
{
name: "Anna",
items: ["Bread", "Cake", "Wine"]
},
{
name: "John",
items: ["Cucumber", "Pizza", "Jam"]
}
]
I've tried this:
class Example extends Component {
render() {
return (
<View>
{
array.map((data) => {
return(
<Text>{data.name}</Text>
{
data.items.map((item) => {
return (
<Text>{item}</Text>
);
}
);
}
}
</View>
);
}
}
I have also tried to put this into a function that I'm rendering but neither is working for me
can you help ?
Maybe this one can help you. Also you should use key otherwise you got warn during rendering.
class Example extends Component {
_renderYYY(item) {
console.log(item);
return item.map((data) => {
return (
<View>
<Text>{data}</Text>
</View>
);
});
}
_renderXXX(array) {
return array.map((data) => {
return (
<View>
<Text key={data.name}>{data.name}</Text>
{
this._renderYYY(data.items)
}
</View>
);
});
}
render() {
return (
<View>
{
this._renderXXX(array)
}
</View>
);
}
}
A limitation of JSX is that a JSX object must always have one single root.
That is,
return (
<Text>One</Text>
<Text>Two</Text>
);
is not valid.
You should wrap the return value of the outer map (including the outer <Text> and the inner result of .map()) with a root element (probably <View>).
Additionally, you should always use the key={} prop, and give it a a globally unique value when rendering an array into JSX.
All in all, I'd have something like this:
class Example extends Component {
render() {
return (
<View>
{
array.map((data) => (
<View key={data.name}>
<Text>{data.name}</Text>
{
data.items.map((item) => (
<Text key={data.name + item}>{item}</Text>
))
}
</View>
))
}
</View>
);
}
}
I'm assuming that there cannot be duplicate names, and that there cannot be duplicate items inside of a single named object.
Here this might help.
class Example extends Component {
renderData = (array) => (
array.map((data, index) => ([
<Text key={index}>{data.name}</Text>,
this.renderItems(data.items)
])
))
renderItems = (items) => (
items.map((item, index) => (
<Text key={`${item}${index}`}>{item}</Text>
)
))
render() {
return (
<View>
{this.renderData(array)}
</View>
);
}
}

Update list when redux store changes

I am trying to update the list when my redux store changes but for some odd reason it isn't. I have to manually refresh the page to see my changes. Here's the snippet of my List component and rowRenderer.
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={visibleRequest.length}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<List
ref={registerChild}
className="List"
height={height}
rowHeight={listRowHeight}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
width={width}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
_rowRenderer = ({ index, key, style }) => {
const { loadedRowsMap, selected } = this.state;
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
} else {
content = (
<div className="placeholder" style={{ width: _.random(100, 200) }} />
);
}
return (
<PendingChat
key={key}
content={content}
style={style}
row={row}
{...this.props}
/>
);
};
Yeah, I ran into the same problem. Its because the references to your objects don't change when you do
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
}
Take a look at immutability.

Can I enclose a flatlist inside a webview somehow?

In my mainscreen App I am rendering a flatlist of articles , I need to change the code so that the urls open inside the App(like webview) . Does anyone know how can I modify the following code ?
render() {
if (this.state.refreshing) {
return (
<View style={styles.refreshing}>
<ActivityIndicator />
</View>
)
} else {
return (
<List>
<FlatList
data={this.state.articles}
renderItem={({ item }) => <Article article={item} />}
keyExtractor={item => item.url}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh.bind(this)}
/>
</List>
);
}
}

Categories