Hello I'm trying to render a fetch response from server like the following
.then(responseData => {
responseData.map(detail => {
let currIndex = -1;
let k = detail.data.curriculum.reduce((acc, curr) => {
if (curr.type === 'section') {
acc.push({
title: curr.title,
content: [],
id: []
})
currIndex += 1
} else {
acc[currIndex].content.push(curr.title);
acc[currIndex].id.push(curr.id);
}
return acc;
}, []);
console.log(k)
this.setState({ k })
});
});
I'm trying to render a UI like this
But what I'm getting is the name of video contents will be listed one after the other as a list like this
The corresponding code I've tried so far is as below
code
<Content padder style={{ backgroundColor: "#f4f4f4" }}>
{this.state.k.map(detail =>
<View style={st.card}>
<View style={st.cardHeading}>
<Text style={st.headingText}>{detail.title}</Text>
</View>
<ScrollView
horizontal
style={st.cardBody}>
<View style={st.videoContent}>
<View style={st.videoImage}>
<MaterialCommunityIcons name="play-circle-outline" size={25} color="#fff" />
</View>
<Text style={st.subheadingText}>{detail.content}</Text>
</View>
</ScrollView>
</View>
)}
</Content
>
The json data within this.state.k is as follows.I'm trying to render the Title as headingText and content as videoContent.Please help me to find a solution.
It looks like what you are missing is to actually loop through the content or id arrays inside the ScrollView element. Based on your current data structure, I would suggest doing something like this.
<ScrollView
horizontal
style={st.cardBody}
>
{detail.content.map((contentValue, i) => (
<View style={st.videoContent}>
<View style={st.videoImage}>
<MaterialCommunityIcons
name="play-circle-outline"
size={25}
color="#fff"
/>
</View>
<Text style={st.subheadingText}>
Title: { contentValue }
Id: { detail.id[i] }
</Text>
</View>
))}
</ScrollView>
Note that I added the value from the id array inside the Text element, both to illustrate how to access it, but also to show that the data structure makes this quite cumbersome so it might be better to improve that structure, possible like this
.then(responseData => {
responseData.map(detail => {
let currIndex = -1;
const k = detail.data.curriculum.reduce((acc, curr) => {
if (curr.type === 'section') {
acc.push({
title: curr.title,
content: []
})
currIndex += 1
} else {
acc[currIndex].content.push(curr);
}
return acc;
}, []);
this.setState({ k })
});
});
That way you don't have to keep track of the index of the array when mapping and can simple use it like this
{detail.content.map((curr) => (
...
<Text style={st.subheadingText}>
Title: { curr.title }
Id: { curr.id }
</Text>
...
))}
Related
Using react native with typescript and redux toolkit
Hi I'm bothering with render a list of messages via FlatList. By ScrollView everything rendering good but I need to implement infiniti scroll. So I'm doing something like this
const MessagesScreen = () => {
const companyId = useAppSelector(getCompanyId);
const userId = useAppSelector(getUserId);
const {
data: messages,
isLoading,
refetch
} = useGetMessagesQuery({ userId, companyId });
useFocusEffect(refetch);
return (
<FlatList
data={messages}
renderItem={() => {
<Messages messages={messages} />;
}}
/>
);
};
In return() I'm trying to render FlatList with component Messages which is down here:
const Messages = ({ messages }: { messages: Message[] }) => {
const navigation =
useNavigation<RootStackScreenProps<'DrawerNavigator'>['navigation']>();
const { colors } = useTheme();
return (
<View style={styles.container}>
{messages.map(message => {
const createdAt = message.created_at;
const isRead = message.read;
const icon = isRead ? 'email-open-outline' : 'email-outline';
const onClick = () => {
navigation.navigate('Message', {
messageId: message.id
});
};
return (
<TouchableOpacity key={message.id} onPress={onClick}>
<View
style={[styles.message, { borderBottomColor: colors.separator }]}
>
<View style={styles.iconPart}>
<Icon
name={icon}
type="material-community"
style={
isRead
? { color: colors.separator }
: { color: colors.inputFocus }
}
size={24}
></Icon>
</View>
<View style={styles.bodyPart}>
<Text
numberOfLines={1}
style={[isRead ? styles.readSubject : styles.unReadSubject]}
>
{message.subject}
</Text>
<Text
numberOfLines={1}
style={[isRead ? styles.readBody : styles.unReadBody]}
>
{message.body}
</Text>
</View>
<View style={styles.datePart}>
<Text style={{ color: colors.shadow }}>
{dayjs(createdAt).fromNow()}
</Text>
</View>
</View>
</TouchableOpacity>
);
})}
</View>
);
};
Actually behaviour is just rendering white screen with error
Possible Unhandled Promise Rejection (id: 17):
Error: Objects are not valid as a React child (found: object with keys {id, msg_type, created_at, subject, body, author, company_id, read}). If you meant to render a collection of children, use an array instead.
there is problem with your call back function:
you are not returning Messages component
1:Remove curly braces
return (
<FlatList
data={messages}
renderItem={() => <Messages messages={messages}/> }
/>
);
2:Add return statement
return (
<FlatList
data={messages}
renderItem={() => {
return <Messages messages={messages} />;
}}
/>
);
Couple things:
You're using the renderItem callback incorrectly:
<FlatList
data={messages}
renderItem={() => {
// ^ ignoring the renderItem props
return <Messages messages={messages} />;
}}
/>
Here, for each item in the messages array, you're rendering a component and passing all the messages into it. So you'll get repeated elements.
The renderItem callback is passed {item, index} where item is the CURRENT item in the array (index is the index into the array)
See docs here:
https://reactnative.dev/docs/flatlist
The usual thing is the renderItem callback renders ONE item at a time, like this:
<FlatList
data={messages}
renderItem={({item}) => {
return <Message message={item} />;
}}
/>
e.g. I'd make a <Message/> component that renders one item only.
I have a object which has an array inside it. Each array has multiple objects. So using the .map function I am looping over the array inside the object. Each array item has a click function where I toggle the item. Initial state of the array item is this
{
DisplayText: "Foresatte"
Selectable: true
UserRecipientToInclude: "IncludeGuardianRecipient"
}
When I do this
choice.Selected = false
and console logs the result the newly added item is not present in the object. Why is that?
Here is the code
{c.UserDropdownMenuChoices.map(choice => {
return ( <TouchableHighlight
{...testProperties(choice.DisplayText, true)}
style={{ opacity: !choice.Selectable ? 0.4 : 1 }}
onPress={() => {
this.selectChoice(c.UserDropdownMenuChoices, choice, c)
}}
underlayColor={'transparent'}
key={choice.DisplayText}
>
<View style={styles.choiceContainer}>
{
choice.selected ? (
//<MaterialCommunityIcons name={'checkbox-marked'} style={styles.checkboxClick} size={20} />
<Icon
type={'material-community'}
name={'checkbox-marked'}
iconStyle={styles.checkboxClick}
size={20}
/>
) : (
<Icon
type={'material-community'}
name={'checkbox-blank-outline'}
iconStyle={styles.checkboxDefault}
size={20}
/>
)
//DONE: <MaterialCommunityIcons name={'checkbox-blank-outline'} style={styles.checkboxDefault} size={20} />
}
<Text style={styles.displayText}>{choice.DisplayText}</Text>
</View>
</TouchableHighlight> )}
and my function is like this
selectChoice(choicesList, choice, contact) {
choice.selected = true
...
console.log(choice) // doesn't have the new property
}
This code is for a react native application
I have previously solved a similar issue by simply scoping out the select hook inside the .map without mapping the attribute onto the array itself.
So what I did was:
import MenuRow from "./menu_row"
...
const Rooms = allRooms.map((room, index) => {
return (
<MenuRow key={room.id} room={room}/>
)
})
Inside MenuRow.js
const MenuRow = (props) => {
let room = props.room
const [selected, setSelected] = useState(true) // Checked by default
return (
<TouchableOpacity onPress={() => {setSelected(!selected), applySelected(room.id, selected) }} style={s.checkrow}>
...
{selected ?
// SELECTED
<View style={s.checked}></View>
:
// NOT SELECTED
<View style={s.unchecked}></View>
}
</TouchableOpacity>
)
However you could also give this a try:
https://stackoverflow.com/a/44407980/4451733
I'm a beginner and I don't know how to change switch value without change all of them. You can see the code about. I'm mapping a nested array on return. I'm confuse how to change the switch status individually. I've tried some solutions like change the value inside the array with an onchange event but it doenst reender the screen.
const [dtData, setDtData] = useState('release_data');
useFocusEffect(
React.useCallback(() => {
getManagerListDay(function(resultado) {
setDtData(resultado);
});
setInterval(() => {
getManagerListDay(function(resultado) {
setDtData(resultado);
});
toasted.showToast('Refresh');
}, 60000);
}, [])
);
const section = [];
for ( var i = 0, ii = dtData.length; i < ii; i++ ) {
if(i>0?dtData[i].dayDate != dtData[i-1].dayDate:true == true){
const items = [];
for ( var b = 0, bb = dtData.length; b < bb; b++ ) {
if(dtData[b].dayDate == dtData[i].dayDate){
items.push({
name: dtData[b].name,
worked: dtData[b].worked,
workedWeekDays: dtData[b].workedWeekDays,
KeyItemDriver: dtData[b].KeyItemDriver
});
}
}
section.push({
dayDate: dtData[i].dayDate,
driverAmount: dtData[i].driverAmountAccepted,
keySectionDay: dtData[i].keySectionDay,
data: items
});
}
}
return (
<Root>
<View style={{marginTop:15}}>
{section.map((obj, index) => {
return(
<List.Section
title={obj.dayDate}
titleStyle={{color:'black',fontWeight:'bold', fontSize: 16}}
style={{backgroundColor:'rgba(79,79,79,0.1)', marginTop:0, borderRadius:30, width:'90%',alignSelf:'center'}}
key={index}
id={obj.keySectionDay} >
<Button mode="contained" style={{borderRadius:70,width:40,backgroundColor:'#48D1CC', marginLeft:'80%'}}>
<Text style={{fontWeight:'bold', marginRight:50}}> {obj.driverAmount} </Text> </Button>
<FAB style={styles.fab} color='white' small={false} icon="database-export" onPress={() => console.log('Pressed')} />
<List.Accordion
title="DRIVERS"
key={index}
theme={{ colors: { primary: '#48D1CC' }}}
titleStyle={{color:'black', fontSize:14}}
left={props => <List.Icon {...props} icon="folder"/>}>
{obj.data.map((dts,index) => {
return(
<List.Item key={index} id={dts.KeyItemDriver} title={dts.name} titleStyle={{color:'black'}}
style={{borderTopColor:'white', borderTopWidth:1}}
left={props => <Switch {...props} color={dts.worked=='S'?'#48D1CC':'red'} value={dts.worked!=null?true:false}/>}
right={props => <Text {...props} style={{color:'#48D1CC',fontSize:18, marginTop:5}} > {dts.workedWeekDays} </Text>}
onPress={() => alert('Sou eu')}
/>
)
})}
</List.Accordion>
</List.Section>
);
})
}
</View>
</Root>
);
Never mind! I was thinking wrong about the steps process. I need.
I just update the information on the database and rerender the screen from there. What I think is correct. I must show what is commited.
Thanks!
I have a screen in which I display a list of products.
I am trying to set up a pagination. I am using List item from react-native-elements and looking at Using RN FlatList as possible in the documentation for this package.
I set up the ability to do pagination, but I got confused in my code. I don't know how to fix it anymore. I would like to know if it would be possible for you to give me a boost and reread my code to give me your opinion.
There for the moment I have the error:
Each child in a list should have a unique "key" prop
I'm a bit lost and need some guidance please. Thanks for any explanation.
The code :
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
productId: (props.route.params && props.route.params.productId ? props.route.params.productId : -1),
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: []
}
};
initListData = async () => {
let list = await getProducts(1);
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 10 ? true : false),
currentPage: 2
});
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 10 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
}
renderItem = ({ item, i }) => (
<ListItem key={i}
bottomDivider
containerStyle={{backgroundColor: i % 2 === 0 ? '#fde3a7' : '#fff' }}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId:parseInt(item.id)})}>
<Icon name='shopping-cart' />
<ListItem.Title style={{width: '65%', fontSize: 14, color: i % 2 === 0 ? '#212121' : '#F78400' }}>{item.name}</ListItem.Title>
<ListItem.Subtitle style={{ color: '#F78400'}}>{i18n.t("information.cost")}:{item.cost}</ListItem.Subtitle>
</ListItem>
);
render() {
//console.log('displayArray', this.state.displayArray)
return (
<View style={{flex: 1}}>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1}}>
<SafeAreaView>
{
this.state.displayArray.map((item, i) => (
<FlatList
keyExtractor={(item, i) => {
return item.id;
}}
data={this.state.displayArray}
onEndReached={() => this.displayBtnLoadMore()}
renderItem={this.renderItem}
/>
))
}
</SafeAreaView>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.container}>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
)}
</View>
);
};
}
The problem is not in your list items but in the FlatList itself - you are rendering an array of FlatList components but they don't have unique keys.
this.state.displayArray.map((item, i) => (
<FlatList
key={item.id} // or key={i} if item doesn't have ID
... rest of your flat list props
/>
))
My app is always loading, When i drop a debugger around and add a watch onto variable list & res , i get not available.
I'm not exactly sure what's the problem or am i even debugging it correctly?
please advice. I'm trying to achieve Loaded by loading the json into the list.
Update:
I just did a console log and saw data
console.log(this.state.list);
var facemashTab = React.createClass({
getInitialState: function() {
return {
list: [],
currentIndex: 0
};
},
componentWillMount: function() {
fetch('https://randomuser.me/api/?results=5')
.then(res => res.json())
.then(res => this.setState({ list: res }));
},
render: function() {
var contents;
if (!this.state.list.length) {
contents = (
<View style={ styles.loading }>
<Text style={ styles.loadingText }>Loading</Text>
<ActivityIndicatorIOS />
</View>
)
} else {
contents = (
<View style={ styles.content }>
<Text>Loaded</Text>
</View>
)
}
return (
<View style={ styles.container }>
<View style={ styles.header }>
<Text style={ styles.headerText }>XXX</Text>
</View>
<View style={ styles.content }>
{ contents }
</View>
</View>
);
}
});
Not sure if this is the problem, but from my understanding setState inside componentWillMount will not trigger a render phase ? could you try componentDidMount