NativeBase unable to list only some part of array - reactjs

So my data is:
[
{"id_categories":1,"name":"One"},
{"id_categories":2,"name":"Two"},
{"id_categories":3,"name":"Three"}
]
And my class to display the list is :
import React, { Component } from 'react';
import { Text } from 'react-native';
import { Container, Content, Card, CardItem, Body, Left,
Right, List, ListItem, } from 'native-base';
export default class ListData extends Component {
render(){
let articles = this.props.data.map(function(items, index){
return(
<List dataArray={items}
renderRow={(item) =>
<ListItem>
<Text>{item}</Text> // this displays the id also
<Text>{item.name}</Text> // this does not display anything
//Only using one of the two <Text> above at a time
</ListItem>
}>
</List>
)
});
return(
<Content>
{articles}
</Content>
)
}
}
I want to be able to show only the name in the list and make it TouchableOpacity and pass the value of the id_categories clicked to another screen. But I'm not able to show the only the name

You don't need to map your array if you use native base list
dataArray contains your array data and renderrow contains a component or custom component.
return (
<List dataArray={this.props.data}
renderRow={(item) =>
<ListItem>
<Text>{item.name}</Text>
</ListItem>
}>
</List>
);

Your item inside map function is already the one you want to process: {"id_categories":1,"name":"One"}.
So inside the map loop you can access name by item.name as follow:
let articles = this.props.data.map(function(item, index) {
// item ={"id_categories":1,"name":"One"},
return(
<List dataArray={"I will skip this one"}
renderRow={(item) =>
<ListItem>
<Text>{item.name}</Text>
</ListItem>
}>
</List>
)
});
EDIT
Just have a look at NativeBase List, and I think this should fit your requirements
let articles = (
<List dataArray={this.props.data}
renderRow={(item) => <ListItem><Text>{item.name}</Text></ListItem>}>
</List>
)
});

Related

How to get current value onClick from an array of items in React js?

I have a list of colors and I'm trying to get the current color onClick from the list but instead I get the entire list of colors. I have a child component where I'm mapping the array:
function Child({colorList, setBackgroundColor}){
return(
<Box>
{colorList.map((i,key)=>
<List key={key}>
<ListItem >
<IconButton icon={<FiCircle fill={i}/>} onClick={setBackgroundColor} color={i}/>
</ListItem>
</List>
</Box>
)
}
and I'm passing the values in the parent component:
function Parent(){
const [bgColor,setBgColor] = useState('')
const [colorList, setColorList] = useState(['blue','red','green'])
function setBackgroundColor(){
const selectedValue = colorList.filter((color,key)=>color[key])
setBgColor(selectedValue)
console.log(bgColor)
}
return(
<Child colorList={colorList} setBackgroundColor={setBackgroundColor}/>
)
}
When I'm explicitly passing the index setBgColor(selectedValue[0]) it works. Can someone please help me understand what I'm doing wrong? Any help would be appreciated!
First - don't call a first param in map method as "i"
if you do map for colors then it is 'color'
Second - You can write:
<IconButton
icon={<FiCircle fill={i} />}
onClick={() => {
setBackgroundColor(i);
}}
color={i}
/>
and you function setBackgroundColor will get your color when you click on that

antd SubMenu "TypeError: Cannot read property 'isRootMenu' of undefined"

I use antd 3.15 and GraphQL to fetch data and generate a list of SubMenu and Menu.Item inside of Menu. However, I got the error message like this Uncaught TypeError: Cannot read property 'isRootMenu' of undefined I have no idea what is wrong with my code. isRootMenu is not a prop listed anywhere on the doc. ant.design/components/menu/#header and when I hardcoded all the SubMenu and Menu.List there is no problem. Can I iterate data from GraphQL to generate the SubMenu and Menu.List?
Can someone help me with this issue, please? Thank you! Here is my code:
import * as React from 'react';
import './SideNav.scss';
import { Menu, Icon } from 'antd';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
const FLOORS_QUERY = gql`
query {
getAllFloors {
id
floorName
rooms {
id
roomName
roomNumber
roomDescription
}
}
}
`;
export default class SideNav extends React.Component {
render() {
return (
<Menu theme="light" defaultSelectedKeys={['1']} mode="inline">
<Query query={FLOORS_QUERY}>
{({ loading, error, data }) => {
if (loading) return <h4> loading... </h4>;
if (error) console.log(error);
console.log(data);
return (
<React.Fragment>
{data.getAllFloors.map((floor) => (
<SubMenu
key={floor.id}
title={
<span>
<Icon type="plus" />
<span>{floor.floorName}</span>
</span>
}
>
<React.Fragment>
{floor.rooms.map((room) => (
<Menu.Item key={room.id}>{room.roomNumber}</Menu.Item>
))}
</React.Fragment>
</SubMenu>
))}
</React.Fragment>
);
}}
</Query>
</Menu>
);
}
}
You should pass the props to the submenu.
const CustomComponent = (props) => (
<Menu.SubMenu title='SubMenu' {...props}>
<Menu.Item>SubMenuItem</Menu.Item>
</Menu.SubMenu>
)
so a solution to your question would be to do the following;
move the query outside of the containing menu
pass the props to the SubMenu
const FloorMapSubMenu = ({ id, floorName, rooms, ...other }) => {
return (
<Menu.SubMenu
key={id}
title={
<span>
<Icon type="plus" />
<span>{floorName}</span>
</span>
}
{...other} // notice the other props, this is were the 'isRootMenu' is injected from the <Menu> children
>
<React.Fragment>
{rooms.map((room) => (
<Menu.Item key={room.id}>{room.roomNumber}</Menu.Item>
))}
</React.Fragment>
</Menu.SubMenu>
)
}
class SideNav extends React.Component {
render() {
return (
<Query query={FLOORS_QUERY}>
{({ loading, error, data }) => {
if (loading) return <h4> loading... </h4>
if (error) console.log(error)
console.log(data)
return (
<Menu theme='light' defaultSelectedKeys={['1']} mode='inline'>
{data.getAllFloors.map((floor, i) => (
<FloorMapSubMenu key={i} id={floor.id} floorName={floor.floorName} rooms={floor.rooms} />
))}
</Menu>
)
}}
</Query>
)
}
}
I found out the Ant design SubMenu needs to use the parent to check some properties like isRootMenu at
SubMenu.js:260
getPopupContainer = props.parentMenu.isRootMenu ? props.parentMenu.props.getPopupContainer : function (triggerNode) {
return triggerNode.parentNode;
}
In order to solve it you need to manually pass parent props into SubMenu like
<Menu.SubMenu {...this.props}/>
to solve the problem. Hope this helps u
Related Github issue item https://github.com/react-component/menu/issues/255
I had this issue while trying to add a <div> as a child of Menu. I just added an empty Menu.Item as the first child of my menu, and the error went away.
<Menu>
<Menu.Item style={{display: 'none'}} />
<div>...</div>
</Menu>
I ran into the same issue. It seems Antd does not allow to place arbitrary components into a Menu/SubMenu. My guess is that Menu.Item needs to get some props, which are passed from Menu/SubMenu to its children.
So you can either create a custom component that passes all props down, or remove the inner <React.Fragment> declaration (the one that is inside the SubMenu), which is not needed anyway.
I was able to make it work by putting the <Query> Component at the top:
<Query query={FLOORS_QUERY}>
{({ loading, error, data }) => {
if (loading) return <Spin />;
if (error) console.log(error);
console.log(data);
return (
<Menu theme="light" defaultSelectedKeys={['1']} mode="inline">
{data.getAllFloors.map((floor) => (
<SubMenu
key={floor.id}
title={
<Link to="/{floor.id}">
<span>
<Icon type="plus" />
<span>{floor.floorName}</span>
</span>
</Link>
}
>
{floor.rooms.map((room) => (
<Menu.Item key={room.id} onClick={this.showRoomProfile}>
{room.roomNumber}
</Menu.Item>
))}
</SubMenu>
))}
</Menu>
);
}}
</Query>
According to the Typescript definitions the childrens of Menu should be of kind Item, SubMenu, ItemGroup or Divider. If you must place a different component on the Header, wrap the Menu and the desired component on a Header component component as such:
import { Layout } from 'antd';
const { Header, Footer, Sider, Content } = Layout;
<Layout>
<Layout.Header>
<div className="logo" />
<Menu>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
</Menu>
<Layout.Header>
</Layout>
I have run into the same issue. But my issues was I have using ternary condition to show some menu's dynamically inside part used the <></> element. That caused me this issue. Once removed that everything work fine.

Pass parameter to component 2 levels deep React Native

Im trying to pass parameters through to a component two levels deep.
I have 2 screens (MainScreen & UserProfileScreen) with a flat list on both screens, both flat lists use the same component EventCard in its renderItem. EventCard is made up of 3 three nested components EventCardHeader,EventCardBody & EventCardFooter. How do I pass certain arguements only from the UserProfileScreen? I have posted code below to give a better understanding of what I have.
MainScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
/>
openEditEvent = (event) => {
this.props.navigation.navigate('EventFormScreen', {
event: event,
eventKey: this.state.eventKey,
editMode: true,
});
};
EventCard
export default class EventCard extends Component {
render() {
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent={() => this.openEditEvent()}
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}
};
EvenCardHeader
export default class EventCardHeader extends Component {
render() {
return (
<CardSection style={styles.eventCardHeader}>
<Thumbnail small
style={styles.eventOrganiserImage}
source={{uri: this.props.eventOrganiserImage}}/>
<Text style={styles.eventPromoterName}>{this.props.eventVenue}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() => this.props.openEditEvent()}>
Edit
</Button>
}
{!!this.props.openDeleteEventAlert &&
<Button onPress={() => this.props.openDeleteEventAlert()}>
Delete
</Button>
}
</CardSection>
);
}
}
I can see that because I have hardcoded the this.openEditEvent() function into my EventCard component that what's causing me the problem, because then the if statement in EventCardHeader that checks if the this.openEditEvent() exists always evaluates to true. Would someone be able to help show me the right way to do this? Thanks in advance for any help.
UPDATE:
Added in openEditEvent
Where is openEditEvent() declared? It should be in the parent component and passed as props to whatever children you need it in. You can continue to pass it as props from children to children.
EDIT:
Ok so you can pass openEditEvent as props like so:
<EventCard
openEditEvent = this.openEditEvent
openEventDetail={() => this.openEventDetail(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
That function can be available in EventCard, and can then be passed AGAIN as props to another child component:
render() {
var openEditEvent = this.props.openEditEvent;
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent = openEditEvent
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}

Switch view depending on button click

I am new to react native.
My question is pretty simple: my screen contains 5 buttons. Each one opens the same < Modal > component. I need to dynamically change the content of the modal, depending on the button clicked.
For example:
if I click the first button, a text input will be shown into the modal.
If I click the second button, checkboxes will be shown into the modal.
Here's my modal :
<Modal
visible={this.state.modalVisible}
animationType={'slide'}
onRequestClose={() => this.closeModal()}>
<View style={style.modalContainer}>
<View style={style.innerContainer}>
<Text>This is content inside of modal component</Text>
<Button
onPress={() => this.closeModal()}
title="Close modal"
>
</Button>
</View>
</View>
</Modal>
Here I open it :
openModal() {
this.setState({ modalVisible: true });
}
Here I call the function (on button press) :
onPress={() => this.openModal()}
I've heard about using props/children, but I don't know how to use them is this case.
Can anyone please help ?
Here is quick example to show who to render different content based on input you provide.
Modal Content
renderModalContent(type, data) {
switch(type) {
1: {
return (
<View>{..data}</View>
)
}
2: {
return (
<Button>...</Button>
)
}
default: (<CustomComponent data={data} />)
}
}
Modal
<Modal>
<View>
{this.renderModalContent(this.state.type, this.state.modalContentData)}
</View>
</Modal>
Here you decide which view you want to render and pass its data.
openModal() {
this.setState({ modalVisible: true, type: 1, data: {...} });
}
You should modify your Modal component so that it renders the base layout with space for dynamic content to be rendered. The content will be passed in as children, via Props. This will mean the modal is dynamic and will / should support future requirements. Try to avoid the switch case in the modal render suggestion unless you have very specific requirements that are unlikely to change in the future, or if you want to do things the React way.
Then for each variant of your Modal (TextInput, Checkbox etc.) create a new Component that wraps the Modal component and have each button initiate rendering the specific component.
If you're using Redux then you would be creating containers, connecting to Redux and passing dynamic state variables. You don't have to use Redux but the principle is the same.
Here's a very basic example to illustrate my point.
// Basic modal that renders dynamic content
const Modal = props => {
const { children } = props;
render (
<View style={styles.modal} >
{children}
</View>
);
}
// Specific modal implementation with TextInput
const ModalWithTextInput = props => (
<Modal>
<TextInput
value={props.someValue}
/>
</Modal>
)
// Specific modal implementation with Switch
const ModalWithSwitch = props => (
<Modal>
<Switch
value={props.someValue}
/>
</Modal>
)
Then in your component that launches the modals, do something like this...
class MyComponent extends Component {
openTextModal = () => {
this.setState({ modalType: 'text' });
}
openSwitchModal = () => {
this.setState({ modalType: 'switch' });
}
renderModal = (type) => {
if (type === 'text') {
return(<ModalWithTextInput />)
}
if (type === 'switch') {
return(<ModalWithSwitch />)
}
}
render() {
const { modalType } = this.state;
render (
<View>
<View>
<TouchableWithX onPress={this.openTextModal} />
<TouchableWithX onPress={this.openSwitchModal} />
</View>
<View>
{this.renderModal(modalType)}
</View>
</View>
);
}
}
Please note this code has not tested but the principle is sound.

Material UI List onClick fires click event on nested list

I have a material ui list in my one of the component. When I click any item of this list, I go to another listView. I am using router to go to another listView. And using onClick method. Whenever I click any list item of first list I print "firstList clicked". and whenever I click any item if second list, it print "secondList clicked".
Here is my Problem:
When I click the ListItem of first list, the console.log("secondList clicked") also gets printed with "firstList Clicked" automatically. I have four list items in second list, so my console print output looks like this
firstList Clicked
secondList Clicked
secondList Clicked
secondList Clicked
secondList Clicked
Why is this happening?
Here is my code.
SecondList code
class TagListItemDetails extends Component {
handleClick() {
console.log("secondList clicked")
}
handleButtonClick() {
browserHistory.push("TagList")
}
render() {
return (
<MuiThemeProvider>
<div>
<List id="parent-list-tags">
<ListItem primaryText="Kitchen" onTouchTap={this.handleClick()}/>
<ListItem primaryText="Beach" onClick={this.handleClick()}/>
<ListItem primaryText="Marriage" onClick={this.handleClick()}/>
<ListItem primaryText="Garden" onClick={this.handleClick()}/>
</List>
<div className="backButton">
<RaisedButton backgroundColor="#293C8E" label="Back" onClick={this.handleButtonClick} labelColor="white">
</RaisedButton>
</div>
</div>
</MuiThemeProvider>
);
}
}
const mapStateToProps =(state) =>{
return {
tags: state.tagReducer
};
};
function matchDispatchToProps(dispatch){
return bindActionCreators({tagSelected: tagSelected}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(TagListItemDetails);
firstList
export default class TagList extends Component {
handleClicked() {
console.log("firstList Clicked")
browserHistory.push("TagListItemDetails")
}
render() {
return (
<div>
<List id="parent-list-tags" >
<ListItem primaryText="Scene" onClick={this.handleClicked} />
<Divider/>
<ListItem primaryText="Actors" onClick={this.handleClicked} />
<Divider/>
<ListItem primaryText="Emotions" onClick={this.handleClicked} />
<Divider/>
<ListItem primaryText="Actions" onClick={this.handleClicked}/>
<Divider/>
<ListItem primaryText="Objects" onClick={this.handleClicked}/>
<Divider/>
<ListItem primaryText="Compliance" onClick={this.handleClicked} />
</List>
<AddButton />
</div>
)
}
};
The problem is that in the SecondList, you are invoking the handleClick method as soon as the component is loaded. Try removing the parentheses () from the onClick handler. So instead of
<ListItem primaryText="Beach" onClick={this.handleClick()}/>
you can use:
<ListItem primaryText="Beach" onClick={this.handleClick}/>
------------------------------------------------------^^ No parentheses here
One way of passing arguments to a click handler passed as a prop is to use the fat arrow function:
onClick={() => this.props.itemSelected(2)}
// or if you want to pass the event as well:
onClick={(event) => this.props.itemSelected(2, event)}
Also, here is a demo of how to fire two functions on onClick event : http://codepen.io/PiotrBerebecki/pen/YGRQrG

Resources