so im trying to get the child values from Firebase and then put it on array then show it as a list, but i can't get it to work.
I used ReactNative Elements Lists for showing the list.
Here's my code
const list = [];
export default class ContactPerson extends Component<Props> {
readData() {
firebase.database().ref('contactperson').on('value', function(snapshot) {
snapshot.forEach(function (childSnapshot) {
var data = childSnapshot.val();
list.push(data);
});
});
}
componentDidMount() {
this.readData();
}
renderRow ({ item }) {
return (
<ListItem
roundAvatar
title={item.nama}
subtitle={item.no_hp}
avatar={{uri:item.image_url}}
/>
)
}
render () {
return (
<List>
<FlatList
data={list}
renderItem={this.renderRow}
keyExtractor={item => item.nama}
/>
</List>
)
}}
And then the Firebase Database structure :
Screenshot
You have to pass the item to the method creating single element as following
renderRow (item) {
return (
<ListItem
roundAvatar
title={item.nama}
subtitle={item.no_hp}
avatar={{uri:item.image_url}}
/>
)}
render () {
return (
<List>
<FlatList
data={list}
renderItem={(item) => this.renderRow(item)}
keyExtractor={item => item.nama}
/>
</List>
)
}}
please refer the documentation for flatlist
Related
I have two components named Home.js and ViewList.js the user can navigate to each component using a stack navigator.
I want to pass the updatePrice function as a parameter from Home.js to ViewList.js below are snippets on what I am attempting to accomplish.
Here is a brief description of how the app is suppose to work. The user navigates from the Home.js component
to the ViewList.js component. In the View.js component the user has the ability to enter a price for the item.
onChangeText I want to run the updatePrice function so that I can update the state of the list item in the Home.js component.
My problem is that I cannot seem to figure out how to pass the updatePrice function to the ViewList through. Any suggestions
on how I can accomplish this ?
Below is a code snippet for Home.js
export default function Home({navigation}){
const updatePrice = (key, id, price) =>{
setShoppingList(list =>
list.map(list =>
list.key === key
? {
...list,
listItems: list.listItems.map(item =>
item.id === id
? {
...item,
price
}
:item
)
}
: list
)
)
console.log(shoppingLists);
}
return(
<View style={globalStyles.container}>
<FlatList
data={shoppingLists}
renderItem={({ item }) =>(
<TouchableOpacity onPress={() =>
// Here I try to pass the function through the
//navigate prop as an object.
navigation.navigate('ViewList',item,{'updatePrice': () => this.updatePrice})}>
{/*Create card container to wrap text in*/}
<Card>
<Text>{item.name}</Text>
<Text>Budget: $ {item.budget}</Text>
</Card>
</TouchableOpacity>
)}
keyExtractor={item => item.key}
/>
</View>
)
}
Here is another ViewList.js
export default function ViewList({navigation}){
const shoppingListKey = navigation.getParam('key');
const updatePrice = navigation.getParam('updatePrice');
const listInfo = navigation.getParam('listItems');
return(
<View style={globalStyles.container}>
<Card>
<Text>{navigation.getParam('name')}</Text>
<Text>Budget: $ {budget}</Text>
<Text> Total: {total}</Text>
<FlatList
data={listInfo}
renderItem={({item}) =>(
<View>
<Text>{item.name}</Text>
<Text>Price: {item.price}</Text>
<TextInput
style={globalStyles.globalTextInput}
keyboardType={'numeric'}
onChangeText={(value) =>
//Here I try to retrieve the updatePrice function but I got an error.
this.props.navigation.state.params.updatePrice(shoppingListKey,item.id,value)}
/>
</View>
)}
keyExtractor={item => item.id}
/>
</Card>
</View>
)
}
I want to paas "subjects" array from SubjectsScreen to MarkAttendanceScreen and display the array items as a FlatList.
Parent Component
export default class SubjectsScreen extends Component {
state = {
subjects: ["A", "B"]
};
render() {
return (
...
<MarkAttendanceScreen subjectsArray={this.state.subjects} />
);
}
}
Child Component
export default class MarkAttendanceScreen extends Component {
constructor(props) {
super(props);
this.state = {
subjects: []
};
}
componentDidMount() {
this.setState({ subjects: this.props.subjectsArray });
}
render() {
return (
<FlatList>
{ this.props.subjects.map((item, key)=>(
<Text key={key}> { item } </Text>)
)}
</FlatList>
);
}
}
Using props was giving error when using FlatList with map.
Works fine when extracting value directly from AsyncStorage.
export default class MarkAttendanceScreen extends Component {
state = {
subjects: [],
text: ""
}
componentDidMount() {
Subjects.all(subjects => this.setState({ subjects: subjects || [] }));
}
render() {
return (
<View>
<FlatList
data={ this.state.subjects}
renderItem={({item}) => {
return (
<View>
<Text> { item.text } </Text>
</View>
)
}}
keyExtractor={ (item, index) => index.toString()}
/>
</View>
);
}
}
let Subjects = {
convertToArrayOfObject(subjects, callback) {
return callback(
subjects ? subjects.split("\n").map((subject, i) => ({ key: i, text: subject })) : []
);
},
convertToStringWithSeparators(subjects) {
return subjects.map(subject => subject.text).join("\n");
},
all(callback) {
return AsyncStorage.getItem("SUBJECTS", (err, subjects) =>
this.convertToArrayOfObject(subjects, callback)
);
},
};
this.props.subjects does not exist, but you did set the state in componentDidMount. In the FlatList use this.state.subject.map.
render() {
return (
<FlatList>
{ this.state.subjects.map((item, key)=>(
// ^here
<Text key={key}> { item } </Text>)
)}
</FlatList>
);
}
You must use the same key name that you used while passing down data to child component e.g. in your case you used key subjectsArray here and You don't need to store this first in state and then use unless you want to update it later.
<MarkAttendanceScreen subjectsArray={this.state.subjects} />
So in your child component, it will be
<FlatList>
{this.props.subjectsArray.map((item, key)=>(
<Text key={key}> { item } </Text>
))}
</FlatList>
D. Smith is correct, you need to change that line to this.state.subjects.map But could also just remove the state variable from the Child Component and use the array directly from props.
export default class MarkAttendanceScreen extends Component {
constructor(props) {
super(props);
}
render() {
return (
<FlatList>
{ this.props.subjectsArray.map((item, key)=>(
<Text key={key}> { item } </Text>)
)}
</FlatList>
);
}
}
Update:
Flatlists need to be defined like this:
<FlatList
data={ this.props.subjectsArray }
renderItem={({item}) => {
return (
<Text> { item } </Text>)
)
}}
keyExtractor={(item, index) => index}
/>
or you can use it the way you have it and remove the flatlist like:
return this.props.subjectsArray.map((item, key)=>(
<Text key={key}> { item } </Text>)
)}
I am trying to change view of ListItem by pressing on it.
In My screen which is normal React component i have functional List component and selectedItemState (only 1 or no items will be selected).
In List there are few also functional ListItem components.
The problem is lack of re-render ability for item.
I've tried memo as official React page says but with no results. Changing components to normal ones gave the same result.
Screen Component:
export default class myScreen extends Component {
constructor () {
super ()
this.state = {
data: [], // <-- there are my objects
isDataEmpty: false,
selectedItemId: ''
}
}
// ... some code
render () {
return (
<View style={styles.container}>
<List
itemList={this.state.data}
onItemPress={ /* go to next screen */}
onItemLongPress={id => {
this.setState({ selectedItemId: this.state.selectedItemId === id ? '' : id })
}}
selectedItemId={this.state.selectedItemId}
/>
</View>
)
}
}
List Component:
const List = props => {
return (
<FlatList
style={style.itemList}
data={props.itemList}
renderItem={info => (
<ListItem
item={info.item}
selectedItemId={props.selectedItemId}
onItemPress={id => props.onItemPress(id)}
onItemLongPress={id => props.onItemLongPress(id)}
/>
)}
/>
)
}
const areEqual = (previous, next) => {
return next.selectedItemId !== '' && (previous.selectedItemId === next.selectedItemId)
}
export default React.memo(List, areEqual)
List Item Component:
const ListItem = props => {
return (
<TouchableWithoutFeedback
onPress={() => props.onItemPress(props.item.id)}
onLongPress={() => {
props.onItemLongPress(props.item.id)
} }>
<View style={style.listItem}>
<Image resizeMode='cover' source={props.item.image} style={style.image} />
<Text>{props.selectedItemId === props.item.id ? 'XXX' : props.item.name}</Text>
</View>
</TouchableWithoutFeedback>
)
}
const areEqual = (previous, next) => {
return next.selectedItemId && (next.selectedItemId === next.item.id)
}
export default React.memo(ListItem, areEqual)
After pressing on any item i want it name to change to 'XXX'. If item will be pressed twice all items should be in normal state
As long as there are no changes on the item itself there will be no rerender of the according listitem.
You could try to force a rerender of the list by changing the value of the extraData flatlist prop though.
I want to add radio button in react native, which is pretty simple and I've done it. But the thing is I want to loop through the radio button upon the contents fetching from a server.So the list depends on the number of items fetching which is changing.The data I'm fetching is as follows
json data
{
"data": [{
"content": "question",
"selection": [
"(a).option1",
"(b).option2 ",
"(c).option3 ",
"(d).option4 "
],
"correct": "4",
}]
}
Well, I'm displaying content inside a CardSection component.I want to loop through the selection array with corresponding radio buttons.I'm using map method for rendering the data. So for selection what should I use?A for loop is okay for that?But I don't know how to do it please do help.Following is the react part.
updated
const formatArray = (arr) => {
let newArr = []
arr.map((item, index) => newArr.push({'label': index, value: item}))
return newArr
}
class QASection extends Component{
render() {
_setActive = (active) => this.setState({this.setState({selected:value})})
return(
{this.state.details.map(a =>
<Card>
<CardSection>
<Text>{a.content}</Text>
</CardSection>
<CardSection>
<RadioForm
radio_props={radio_props}
initial={0}
itemShowKey="label"
itemRealKey="value"
dataSource={formatArray(data)}
onPress={onPress}
/>
<CardSection data={data} key={index} onPress={this._setActive} />
</Card>
)}
);
}
}
By rendering selection[0] it will give me the first value in selection array. But how do I loop through this with radio buttons for each item in an array?
You can loop in the following way
I assume that you are using react-native-radio-form
const formatArray = (arr) => {
let newArr = []
arr.map((item, index) => newArr.push({'label': index, value: item}))
return newArr
}
const YourCardComponent = ({data, onPress}) => {
return (
<Card>
<CardSection>
<Text>{data.content}</Text>
</CardSection>
<CardSection>
<RadioForm
radio_props={radio_props}
itemShowKey="label"
itemRealKey="value"
dataSource={formatArray(data.selection)}
onPress={onPress}
// ... other props as required
/>
</CardSection>
</Card>
)
}
// Inside the class
_setActive = (active) => this.setState({selected:value})
render () {
const {details} = this.state
return (
<View>
{details.map((data, index) => (
<YourCardComponent data={data} key={index} onPress={this._setActive} />
))}
</View>
)
}
I'm learning react and I try to create simple TODO based on material-ui, I have problem with handling IconMenu menu actions, menu is displayed in listItem element. At this moment I have no idea how trigger deleteItem function with item name as a parameter when delete action is clicked in menu.
const iconButtonElement = (
<IconButton touch={true} tooltip="More" tooltipPosition="bottom-left">
<MoreVertIcon color="black"/>
</IconButton>
);
const rightIconMenu = (
<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem value="done" leftIcon={<Done />}>Mark as done</MenuItem>
<MenuItem value="delete" leftIcon={<Delete />}>Delete</MenuItem>
</IconMenu>
);
class TodoElements extends Component {
deleteItem(nameProp)
{
this.props.delete(nameProp);
}
render() {
var listItemRender = function(item) {
return <ListItem key={item.name} primaryText={item.name} style={listItemStyle} rightIconButton={rightIconMenu}/>
};
listItemRender = listItemRender.bind(this);
return (
<List>
{this.props.items.map(listItemRender)}
</List>
)
}
}
As far as I can see, you should be able to add an onChange handler to your IconMenu. So your rightIconMenu can look like this:
const RightIconMenu = ({onChange}) => (
<IconMenu iconButtonElement={iconButtonElement} onChange={onChange}>
<MenuItem value="done" leftIcon={<Done />}>Mark as done</MenuItem>
<MenuItem value="delete" leftIcon={<Delete />}>Delete</MenuItem>
</IconMenu>
);
Then you can use it in your TodoElements like this:
class TodoElements extends Component {
constructor(props){
super(props);
this.state = {
items: props.items
};
}
createChangeHandler = (nameProp) => {
return (event, value) => {
if(value==="delete"){
this.deleteItem(nameProp);
}
};
}
deleteItem = (nameProp) =>
{
this.setState({
items: this.state.items.filter((item) => {
return item.name !== nameProp);
})
});
}
render() {
return (
<List>
{this.state.items.map((item) => {
<ListItem key={item.name} primaryText={item.name} style={listItemStyle}
rightIconButton={<RightIconMenu onChange={this.createChangeHandler(item.name)} />}/>
})}
</List>
)
}
}
Alternative
As an alternative solution you could bind an onClick handler to your delete MenuItem instead. I would probably implement it like this:
const RightIconMenu = ({onDelete}) => (
<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem value="done" leftIcon={<Done />}>Mark as done</MenuItem>
<MenuItem value="delete" leftIcon={<Delete />} onClick={onDelete}>Delete</MenuItem>
</IconMenu>
);
And then replace the appropriate functions in the TodoElements:
createChangeHandler = (nameProp) => {
return (event, value) => {
this.deleteItem(nameProp);
};
}
render() {
return (
<List>
{this.state.items.map((item) => {
<ListItem key={item.name} primaryText={item.name} style={listItemStyle}
rightIconButton={<RightIconMenu onDelete={this.createDeleteHandler(item.name)} />}/>
})}
</List>
)
}
As for handling the state of your list of items, you should probably take a look at global state management such as Redux.
I think that a nicer approach would be using the onTouchTap every MenuItem has, So the onChange function won't have a switch or many if statements.
I'm actually using it when I iterate over all menu items,
To me it looks like this:
_.map(menuItems, (currItem, index) => {
return (<MenuItem primaryText={currItem.primaryText}
rightIcon={currItem.rightIcon}
leftIcon={currItem.leftIcon}
key={`menu-item-${index}`}
value={currItem.value}}
onTouchTap={currItem.onTouchTap}/>)
})