I have an array stored in my redux store, but when I call notifications.map((x, i) => {} nothing actually renders to the view... however if I console.log x to the console then they print....
How do I get my array contents to render to the view?
import React from 'react'
import { connect } from 'react-redux'
import {List, ListItem} from 'material-ui/List'
const mapStateToProps = state => {
return {
notifications: state.notificationsReducer.notifications,
errorMessage: state.notificationsReducer.errorMessage
}
}
const notifications = ({notifications, errorMessage}) => {
notifications.map((x, i) => {
console.log(x.title)
})
return (
<List>
{notifications.map((x, i) => {
<ListItem key={i} primaryText={x.title} />
})}
</List>
)
}
const Notifications = connect(mapStateToProps)(notifications)
export default Notifications
Remove the brackets of arrow function inside the map.
<List>
{notifications.map((x, i) =>
<ListItem key={i} primaryText={x.title} />
)}
</List>
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Function_body
I Arrow functions can have either a "concise body" or the usual "block
body".
In a concise body, only an expression is needed, and an implicit
return is attached. In a block body, you must use an explicit return
statement.
you have to return a value from the function to get the result you want
return (
<List>
{notifications.map((x, i) => {
return <ListItem key={i} primaryText={x.title} />
})}
</List>
)
or simply by not opening a curly brackets in the first place (implicit return)
return (
<List>
{notifications.map((x, i) =>
<ListItem key={i} primaryText={x.title} />
)}
</List>
)
Related
I want the category details in the parent categoryHandler function
from the child component. I don't know where to place this
props.categoryHandler function in the child component so that when it
is clicked I should get the details to the parent categoryHandler
function.
PARENT COMPONENT:
const categoryHandler = (catg) => {
console.log(catg);
}
<div className="categoryBox">
<Category categories={categories} categoryHandler={() => categoryHandler} />
</div>
CHILD COMPONENT:
export default function Category({ categories }) {
if (categories.length) {
const menu = recursiveMenu(categories);
return menu.map((item, key) => <MenuItem key={key} item={item} />);
} else {
return <div>No Menus</div>
}
}
const MenuItem = ({ item }) => {
const Component = hasChildren(item) ? MultiLevel : SingleLevel;
return <Component item={item} />;
};
const SingleLevel = ({ item }) => {
return (
<ListItem button>
<ListItemText className="category-link child" primary={item.title} />
</ListItem>
);
};
const MultiLevel = ({ item }) => {
const { items: children } = item;
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen((prev) => !prev);
};
return (
<React.Fragment>
<ListItem button onClick={handleClick}>
<ListItemText className="category-link parent" primary={item.title} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{children.map((child, key) => (
<MenuItem key={key} item={child} />
))}
</List>
</Collapse>
</React.Fragment>
);
};
Your approach in the code is right, just you have to modify two thing to achieve what you are expecting.
In the parent component you have to modify passing the function as a props like this:
categoryHandler={categoryHandler}
In the child component you have to catch the function while destructuring the props and call in on the both list item with the single item as function parameter:
add the function in props destructuring and pass the function as another props to MenuItem
export default function Category({ categories, categoryHandler }) {
if (categories.length) {
const menu = recursiveMenu(categories);
return menu.map((item, key) => <MenuItem categoryHandler={categoryHandler} key={key} item={item} />);
} else {
return <div>No Menus</div>
}
}
Now again pass the function props to Single And MultiLevel List and call the function on both place:
const MenuItem = ({ item, categoryHandler }) => {
const Component = hasChildren(item) ? MultiLevel : SingleLevel;
return <Component item={item} categoryHandler={categoryHandler} />;
};
const SingleLevel = ({ item, categoryHandler }) => {
return (
<ListItem button onClick={handleClick}>
<ListItemText className="category-link child" primary={item.title} />
</ListItem>
);
};
const MultiLevel = ({ item, categoryHandler}) => {
const { items: children } = item;
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen((prev) => !prev);
};
return (
<React.Fragment>
<ListItem button onClick={handleClick}>
<ListItemText className="category-link parent" primary={item.title} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{children.map((child, key) => (
<MenuItem key={key} item={child} />
))}
</List>
</Collapse>
</React.Fragment>
);
};
This solution should work fine!
When you call your function passed as a prop, you can pass data from a child to parent component.
Parent Component:
const categoryHandler = (catg) => {
console.log(catg);
}
<div className="categoryBox">
<Category categories={categories} categoryHandler={categoryHandler} />
</div>
Child Component:
export default function Category(props) {
props.categoryHandler(data);
}
You can need to pass the parameter to the function in your parent component like this:
<Category categories={categories} categoryHandler={categoryHandler} />
You need to pass the prop categoryHandler all the way to the SingleLevel like this:
categoryHandler={categoryHandler}
You can need to add onClick to the ListItem in your SingleLevel component with item parameter like this:
<ListItem button onClick={() => categoryHandler(item)}>
You can take a look at this sandbox for a live working example of this solution.
I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}
I'm fetching some data from the firebase realtime database. I have created a state in the constructor and initialised as an empty array. Later in the componentDidUpdate method, I have updated the state with setState method. The issue is the render method called twice in the component and data is getting multiplied each time.
this.state = {
values: [],
}
componentDidMount = () => {
firebase.database().ref('Table').once('value', (data) => {
var input = data.val();
this.setState({ values: input })
})
}
And the render method:
var val = []; //global variable declared before class declaration
render() {
{
this.state.values.map(item => {
val.push(
<List>
<ListItem>
<Text>{item["value"]}</Text>
</ListItem>
</List>
)
})
}
return(
<View>
{val}
</View>
)
}
And the list item is keep getting multiplied each time when the component renders. I have checked the doc but couldn't get a proper solution.
https://reactjs.org/docs/react-component.html#componentdidmount
Where is val defined?
Okay. That I have defined a global var. Declared it as an array before the class declaration
That's where your duplication comes from.
Better do it this way:
render() {
const val = this.state.values.map((item, index) => (
<List key={index}>
<ListItem>
<Text>{item.value}</Text>
</ListItem>
</List>
));
return <View>{val}</View>;
}
I didn't understand well the val variable but this code should work for you:
mapValues = list => list.map((item, index) => (
<List key={index}>
<ListItem>
<Text>{item.value}</Text>
</ListItem>
</List>
));
render() {
return (
<View>
{this.mapValues(this.state.values)}
</View>
);
}
Really stumped. I am trying to create a ListItem for every key of every value in an array of objects. When I log item, it returns the key I'm looking for as a string. Great! However, the list items never render on the page.
return (
<div>
<List className="list">
{Object.values(props.sectionInfo).forEach(section => {
Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'> //doesn't render, but also doesn't throw errors
<ListItemText primary={item} />
</ListItem>
)
});
})}
</List>
</div>
);
console.log(item) //returns "red", "blue"
The below renders the list perfectly, however the list items are the indexes (0, 1)
return (
<div>
<List className="list">
{Object.keys(props.sectionInfo).map((section, index) => {
return (
<ListItem button className='list-item'>
<ListItemText primary={section} />
</ListItem>
)
})}
</List>
</div>
);
Any insight would be helpful.
This is because you are using the forEach in the outer loop and it doesn't return anything actually, so the children prop of the List is undefined. Try this:
return (
<div>
<List className="list">
{Object.values(props.sectionInfo).map(section => {
return Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'>
<ListItemText primary={item} />
</ListItem>
)
});
})}
</List>
</div>
);
Please try to build list of virtual doms as following:
let items = []
Object.values(props.sectionInfo).forEach(section => {
let subItems = Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'> //doesn't render, but also doesn't throw errors
<ListItemText primary={item} />
</ListItem>
)
});
items = items.concat(subItems);
})
return (
<div>
<List className="list">
{items}
</List>
</div>
);
Have you tried going through Object.values(section) in the second loop?
Because from your second statement it seems like the contents are indexed as an Array. Maybe you can give more information about the data structure to help you further.
Im working on a project where im creating swipe card effect and im getting failed to compile when running npm run start:dev on the app
import React from 'react';
import Cards, { Card } from 'react-swipe-card'
const data = ['Alexandre', 'Thomas', 'Lucien']
const SwipeCard = () => (
return (
<Cards onEnd={action('end')} className='master-root'>
{data.map(item =>
<Card
onSwipeLeft={action('swipe left')}
onSwipeRight={action('swipe right')}>
<h2>{item}</h2>
</Card>
)}
</Cards>
)
);
export default SwipeCard;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Doc can be found here
You are using an arrow function without a body for your SwipeCard component which has an implicit return, so you can just remove the return statement.
const SwipeCard = () => (
<Cards onEnd={action("end")} className="master-root">
{data.map(item => (
<Card
key={item}
onSwipeLeft={action("swipe left")}
onSwipeRight={action("swipe right")}
>
<h2>{item}</h2>
</Card>
))}
</Cards>
);
As mentioned by #Tholle you can simply remove the return statement. Alternatively you can change the outer ( and ) to { and } as shown below to imply a function. I generally do this to have a consistent format across all my arrow functions.
import React from 'react';
import Cards, { Card } from 'react-swipe-card'
const data = ['Alexandre', 'Thomas', 'Lucien']
const SwipeCard = () => {
return (
<Cards onEnd={action('end')} className='master-root'>
{data.map(item =>
<Card
onSwipeLeft={action('swipe left')}
onSwipeRight={action('swipe right')}>
<h2>{item}</h2>
</Card>
)}
</Cards>
)
};
export default SwipeCard;