I have a component that takes the props. If it's "1", it should use dataOne selector in order to use the right selector with filters. If it's "2" then use dataTwo and so on.
For example:
const [dataSet] = useState(route.params.dataSet);
const dataOne = useSelector(selectDataOne);
const dataTwo = useSelector(selectDataTwo);
const dataThree = useSelector(selectDataTwo);
const dataFour = useSelector(selectDataTwo);
The problem is that I want to map through one of these selectors depending on the prop and I want an elegant solution for it as dataSet[0].map while dataSet === "dataOne" will not work of course. What would be the alternative? What would be the prefer way to do so? Create a separate component with the switch statement? I want an elegant solution instead of spamming ternary operators like:
{dataSet == "1" && dataOne.map etc.}
{dataSet == "2" && dataTwo.map etc.}
What would be the best way to take the name from the props and map through it?
EDIT: For now I have this working solution, but not sure if it's the prettiest one:
const Data = (dataSet: string) => {
switch (dataSet) {
case "1":
return dataOne.map((c: any, i: number) => {
const card = c.dataSet === 1 && ;
if (i === dataOne.length - 1) {
return (
<View key={i} style={styles.lastMargin}>
<Card key={i} data={c} isOperating={c.isOperating} />
</View>
);
}
return card;
});
case "2":
return dataTwo.map((c: any, i: number) => {
const card = c.dataSet === 2 && <Card key={i} data={c} isOperating={c.isOperating} />;
if (i === dataTwo.length - 1) {
return (
<View key={i} style={styles.lastMargin}>
<Card key={i} data={c} isOperating={c.isOperating} />
</View>
);
}
return card;
});
case "3":
return dataThree.map((c: any, i: number) => {
const card = c.dataSet === 3 && <Card key={i} data={c} isOperating={c.isOperating} />;
if (i === dataThree.length - 1) {
return (
<View key={i} style={styles.lastMargin}>
<Card key={i} data={c} isOperating={c.isOperating} />
</View>
);
}
return card;
});
case "4":
return dataFour.map((c: any, i: number) => {
const card = c.dataSet === 4 && <Card key={i} data={c} isOperating={c.isOperating} />;
if (i === dataFour.length - 1) {
return (
<View key={i} style={styles.lastMargin}>
<Card key={i} data={c} isOperating={c.isOperating} />
</View>
);
}
return card;
});
}
};
You definitely shouldn't repeat your render logic. One way to do this could be by mapping each prop to a selector, getting the right one into a variable, and then using that in the render.
// map each prop to a selector
const selectorsMap = {
"1": dataOne,
"some-value": dataTwo,
...
};
// access the correct selector based on the prop
const data = selectorMap[dataSet];
return data.map(...);
Related
I have a component which lists items with checkbox, I would like to check /uncheck with
clicked, but couldn't remove a duplicate item and the checkbox doesn't work. Any help would be appreciated
Checkbox.tsx
<TouchableOpacity
onPress={onPress} >
{selected && <Icon size={iconSize} color={color} name={'check'} />}
</TouchableOpacity>
App.tsx
{data.map((item, index) => (
<View}>
<CheckBox
boxSize={Layout.baseUnit * 6}
iconSize={Layout.baseUnit * 4}
color={Color.Dark}
selected={false}
onPress={() => {
handleSelected(item, index)
}}
/>
</View>
const handleSelected = (listItem: any, index: number) => {
newUpdatedList.push({
addedItem: listItem,
checked: true,
});
if (newUpdatedList.length > 0) {
newUpdatedList.findIndex(element => {
return element.addedItem === listItem;
}) !== -1;
}
const test = newUpdatedList.filter(element => {
return element.addedItem.toLowerCase() !== listItem.toLowerCase();
});
setNewUpdatedList([...test]);
};
I have a wrapper component that receives data from another component, which is generated in the component even higher up the hierarchy from the global state.
const HubList: FC<IHubList> = ({ items, size, loading, error }) => {
const { history } = useRouter()
if (loading === 'loading') {
return <Spinner />
} else if (loading === 'error') {
return <h5 className="text-center mt-5">{error}</h5>
}
const renderItems = (arr: IListing[]) => {
if (arr.length === 0) {
return (
<CSSTransition timeout={0} classNames="hero">
<h5 className="text-center mt-5">Empty</h5>
</CSSTransition>
)
}
return (
<Box className="space-y-4">
<TransitionGroup component={null}>
{arr.slice(0, size).map((item, idx) => {
return (
<CSSTransition
onEntering={() => console.log('enter')}
timeout={5000}
key={item.id}
classNames="hero"
mountOnEnter
>
<Card challenge={item} action={() => history.push(item.slug)} />
</CSSTransition>
)
})}
</TransitionGroup>
</Box>
)
}
const elements = renderItems(items)
return elements
}
export default HubList
But it doesn't work in any way.
I have an object of arrays that I'm mapping over called featuredProj, and on alternate items, I want the CSS to conditionally render as not to repeat so much code. Using useState in the handleSide function I get the error too many rerenders. How can I solve this, or is there a better solution to rendering jsx while mapping over an array of objects.
const useStyles = makeStyles(() => ({
title: {
textAlign: (side) => (side ? "right" : "left"),
},
}));
const FeaturedProjects = () => {
const [side, setSide] = useState(true);
const classes = useStyles(side);
const handleSide = (project, index) => {
if (index === 0 || index % 2 === 0) {
// I tried setSide(false), setSide(prev => !prev)
return (
<Grid Container key={index}>
<Typography className={classes.title}>{project.title}</Typography>
</Grid>
);
} else {
return (
<Grid Container key={index}>
<Typography className={classes.title}>{project.title}</Typography>
</Grid>
);
}
};
return (
<Container>
{featuredProj.map((proj, ind) => (
<Reveal duration="2000" effect="fadeInUp">
{handleSide(proj, ind)}
</Reveal>
))}
</Container>
);
};
Thanks in advance for any assistance!
You cannot call setState() inside render method. setState() will trigger a re-rendering which calls render method again which leads to another setState().. you get the idea.
If you want it to work, you need to create separate component with the side props and pass it as an argument to your style hook.
const FeaturedProjects = () => {
const classes = useStyles(side);
return (
<Container>
{featuredProj.map((proj, ind) => (
<FeaturedProjectItem
key={index}
project={proj}
side={index === 0 || index % 2 === 0}
/>
))}
</Container>
);
};
const useStyles = makeStyles({
title: {
textAlign: (side) => (side ? "right" : "left"),
},
});
const FeaturedProjectItem = ({ side, project }) => {
const classes = useStyles(side);
return (
<Reveal duration="2000" effect="fadeInUp">
<Grid Container>
<Typography className={classes.title}>{project.title}</Typography>
</Grid>
</Reveal>
);
};
I have subdivided my components and I want to change state of text using deleteName function from child component. However I have used onPress={this.props.delete(i)} in my child component which is not working. The error that occurs for me is:
undefined variable "I"
Here is my code:
App.js
export default class App extends Component {
state = {
placeName: '',
text: [],
}
changeName = (value) => {
this.setState({
placeName: value
})
}
deleteName = (index) => {
this.setState(prevState => {
return {
text: prevState.text.filter((place, i) => {
return i!== index
})
}
}
}
addText = () => {
if (this.state.placeName.trim === "") {
return;
} else {
this.setState(prevState => {
return {
text: prevState.text.concat(prevState.placeName)
};
})
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<Input changeName={this.changeName}
value={this.state.placeName} />
<Button title="Send" style={styles.inputButton}
onPress={this.addText} />
</View>
<ListItems text={this.state.text} delete={this.deleteName}/>
{/* <View style={styles.listContainer}>{Display}</View> */}
</View>
);
}
}
and child component ListItems.js
const ListItems = (props) => (
<View style={styles.listitems}>
<Text>{this.props.text.map((placeOutput, i) => {
return (
<TouchableWithoutFeedback
key={i}
onPress={this.props.delete(i)}>
onPress={this.props.delete}
<ListItems placeName={placeOutput}/>
</TouchableWithoutFeedback>
)
})}
</Text>
</View>
);
You need to bind the index value at the point of passing the props to the child.
delete = index => ev => {
// Delete logic here
}
And in the render function, you can pass it as
items.map((item, index) => {
<ChildComponent key={index} delete={this.delete(index)} />
})
In your child component, you can use this prop as
<button onClick={this.props.delete}>Click me</button>
I have created a Sandbox link for your reference
Instead of onPress={this.props.delete(i)}, use onPress={() => this.props.delete(i)}
In order to have the cleaner code, you can use a renderContent and map with }, this);like below. Also you need to use: ()=>this.props.delete(i) instead of this.props.delete(i) for your onPress.
renderContent=(that)=>{
return props.text.map((placeOutput ,i) => {
return (
<TouchableWithoutFeedback key={i} onPress={()=>this.props.delete(i)}>
onPress={this.props.delete}
</TouchableWithoutFeedback>
);
}, this);
}
}
Then inside your render in JSX use the following code to call it:
{this.renderContent(this)}
Done! I hope I could help :)
I was working on a react-native application and I created a common component for display list items.
<View style={styles.container}>
<ItemsWithSeparator style={styles.itemsContainer}>
<AppRow />
<AppRow />
</ItemsWithSeparator>
</View>
Now, my ItemListSeparator is just iterates over the children and renders the list, so I thought I would make this a stateless component.
const ItemsWithSeparator = function ({children,style}) {
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
};
But this throws an error saying 'React' not found.
However, it works fine with class based components. Following is the code which is working fine.
class ItemsWithSeparator extends React.Component {
render () {
const {children,style} = this.props;
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
}
}
Can anyone help me understanding this? TIA!!
Update:
I was just trying few something and apparently got his to work:-
const ItemsWithSeparator = function ({children,style,...props}) {
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
{...props}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
};
But I am still a bit confused on how is this working. If someone could explain I would really be great.
Here is refactored version so you don't have to do this weird React.Children stuff :D Notice that you can return array when mapping children. There you can make if statements if needed.
const ItemsWithSeparator = ({children, style, ...props}) => {
const finalFields = children.map((child, index) => {
return [
child,
index !== children.length - 1 && (
<View key={index} {...props} style={styles.separator} />
)
];
});
return (
<View style={style}>
{finalFields}
</View>
);
};