I have an icon position prop that decide whether it's placed on the left or on the right of the children. I have this working
<List>
{iconPosition === 'right' && (
<Text />
)}
{icon && (
<Icon />
)}
{iconPosition === 'left' && (
<Text />
)}
</List>
But I think it can be more simple, although in my opinion my above code is readable.
You can't do this any other way. You're using individual conditions with their own result.
Maybe you could place them on one line to make them more readable.
<List>
{iconPosition === 'right' && <Text />}
{icon && <Icon />}
{iconPosition === 'left' && <Text />}
</List>
or
render() {
const textLeft = (iconPosition === 'left');
const textRight = (iconPosition === 'right');
...
}
And use that. But that's mostly down to your preference / the coding style you and your colleagues are using..
One way to do it is to use css float (left/right) but it'll make the icon to be all the way to the right. Another way is to use flex box, take for example:
<div style="display: flex; flex-direction: row;">
<div style="flex: 0; order: 1;">(X)</div>
<div>content</div>
</div>
The (X) is the "icon". The order: 1; in the style moves it to the right, if you change it to 0 or ommit it, it'll be on the left.
so if you use this approach and put the text + icon in a flex row, all you need to do is:
<Icon style={{order: (iconPosition === 'right' ? 1 : 0)}} />
You could introduce a higher-order component to attempt to deal with it. It does add some complexity to the project though.
const WithConditionalRender = WrappedComponent => (
({ renderCondition, ...props }) => {
if (renderCondition) {
return <WrappedComponent { ...props } />;
}
return null;
}
);
const ConditionalText = WithConditionalRender(Text);
const ConditionalIcon = WithConditionalRender(Icon);
const Example = ({ icon, iconPosition }) => (
<List>
<ConditionalText renderCondition={ iconPosition === 'right' } />
<ConditionalIcon renderCondition={ icon } />
<ConditionalText renderCondition={ iconPosition === 'left' } />
</List>
);
This is a generic HOC and could be tweaked to be more specific.
Related
<ThemeProvider theme={theme}>
<GlobalStyle />
{componentName !== 'questionaire' &&
componentName !== 'activityResult' && <CardWrapper />}
<ErrorModal
errorModal={errorModal}
handleErrorModal={handleError}
errorMsg={error}
/>
{successModal && successMsg ? (
<SuccessModal successModal={successModal} successMsg={successMsg} />
) : (
<Grid
className="card-layout"
style={
componentName === 'questionaire'
? { margin: '20px', height: 'calc(100% - 40px)' }
: { margin: '30px 20px' }
}
>
{customTagProps.meterId && (
<CustomTag type={componentName} propData={customTagProps} />
)}
</Grid>
)}
</ThemeProvider>
I have a modal component, which on load re renders the entire component. I want to prevent the re render of the entire component.
lack of Information
may I only suggest you try using useRef instead of useState
you could use useMemo() to return the component
I am implementing a custom button that uses a <Pressable/> as it's base component.
//...
<Pressable
style={({ pressed }) => [
styles.button,
style,
// if button is disabled it gets grayed out
// if it's pressed, and user did not specify underlay color
// it uses default overlay color
// if it's not pressed, use either user-specified
// background color, or default
{
backgroundColor: isDisabled
? "#808080"
: pressed && underlayColor !== false
? underlayColor || styles.button.underlayColor
: style?.backgroundColor || styles.button.backgroundColor,
},
]}
//...
I feel like this is unreadable.
Furthermore, I have another custom button component that uses this component (custom-custom component, so to speak). If I pass an array as a style to it, the overlay feature won't work since it is expecting an object (style?.backgroundColor), not an array.
Here is the source code for both components:
Custom buttom:
const Button = ({
title,
containerStyle,
style,
onPress,
isDisabled,
textStyle,
icon,
iconContainerStyle,
underlayColor,
android_ripple,
}) => {
return (
<View style={[styles.container, containerStyle]}>
<Pressable
style={({ pressed }) => [
styles.button,
style,
{
backgroundColor: isDisabled
? "#808080"
: pressed && underlayColor !== false
? underlayColor || styles.button.underlayColor
: style?.backgroundColor || styles.button.backgroundColor,
},
]}
android_ripple={
android_ripple === true && { color: "rgba(0, 0, 0, 0.2)" }
}
disabled={isDisabled}
onPress={onPress}
>
<>
{/* show icon if specified */}
{icon ? (
<View style={[styles.icon, iconContainerStyle]}>{icon}</View>
) : null}
{/* show title if specified */}
{title && (
<Text
style={{
...styles.text,
...textStyle,
}}
>
{title}
</Text>
)}
</>
</Pressable>
</View>
);
};
export default Button;
Icon Button (custom-custom button):
export default function IconButton({
containerStyle,
style,
onPress,
isDisabled,
iconName,
iconType,
iconColor,
iconSize,
}) {
return (
<Button
isDisabled={isDisabled}
containerStyle={{ ...styles.container, ...containerStyle }}
/*[styles.button, style] will break the overlay color feature.. since in the custom <Button /> component, it is expecting an object, not an array*/
style={{ ...styles.button, ...style }}
onPress={onPress}
icon={
<Icon
name={iconName}
type={iconType}
color={iconColor}
size={iconSize || 35}
/>
}
underlayColor={false}
android_ripple={true}
/>
);
}
I want to be able to pass arrays and still be able to access the styles.backgroundColor. What would be the best of way of doing this?
Thanks in advance!
I have the following logic inside a react component where, I am rendering different component based on the boolean values. This peace of code is very difficult to understand. Are there anyways, I can simply that logic:
{isEnabled ? (
<>
{!loading ? (
<>
{items.length === 0 ? (
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>
) : (
<ComponentTwo/>
)}
</>
) : (
<div>
<LoadingComponent/>
</div>
)}
</>
) : (
<ComponentThree/>
)}
I'd probably split it up into seperate components and pass parameters down the component tree for example
{isEnabled ? <IsLoadingComponent loading={loading} items={items}> : <ComponentThree/>}
You might find it useful to split the component up into a "Loading" version and a "Loaded" version so you don't have to handle both states in the same component. Then the component basically just renders the "Loading" or "Loaded" version depending on the flag.
But even without that, you can at least make that easier to debug by using if/else if etc. and assigning to a temporary variable:
let comp;
if (isEnabled) {
if (loading) {
comp = <div>
<LoadingComponent/>
</div>;
} else if (items.length === 0) {
comp = <>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>;
} else {
comp = <ComponentTwo />;
}
} else {
comp = <ComponentThree />;
}
Then just
{comp}
where that nested conditional was.
I think you are making a simple thing very complicated. What we can do instead is that make use of "&&".
{ isEnabled && loading && <LoaderComponent /> }
{isEnabled && !items.length &&
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>
}
{isEnabled && items.length && <ComponentTwo/>}
{!isEnabled && <ComponentThree />}
Though I want to support the argument the others made (split into multiple components), you can already achieve a bit more readability by dropping unnecessary fragments (<></>) and/or parenthesis and by using "better"(opinion) indentation.
return (
isEnabled
? loading
? <div><LoadingComponent/></div>
: items.length === 0
? <> {/* this is the only place a fragment is actually needed */}
<ComponentOne/>
<Container>
<img src={Image} alt="Image"/>
</Container>
</>
: <ComponentTwo/>
: <ComponentThree/>
);
Alternatively, early returns do help a lot with readability. For example:
const SomeComponent = () => {
// ...snip...
if (!isEnabled) {
return <ComponentThree/>;
}
if (loading) {
return <div><LoadingComponent/></div>;
}
if (items.length > 0) {
return <ComponentThree/>;
}
return (
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image"/>
</Container>
</>
);
}
I'm using a react-native-elements ListItem.Accordion because everything else in my React Native SectionList uses ListItems (and the documentation seems un-opinionated about this), and I'm very happy with the rendering. Unfortunately, when scrolling down it scrolls beyond the last displayed item until it gets to where the end would be if all the accordion items were fully expanded.
Obviously, when all the accordion items are expanded it stops scrolling when it gets to the end.
How do I get the SectionList to only scroll to what's visible when the accordion items are not expanded?
const renderSectionHeader = ({ section: { title, data } }) => {
return { data.length > 0 ? (
<Text>{title}</Text>
) : null };
}
const renderSeparator = () => {
return (
<View style={{
height: 1,
backgroundColor: "#CED0CE",
marginLeft: "5%",
width: "90%",
}} />
);
};
const renderItem = ({ item }) => {
return (
<ListItem.Accordion
content={
<>
<MaterialIcons.Button
name="shopping-basket"
style={{paddingLeft:20}}
onPress={() => { alert(item.greeting) }} />
<ListItem.Content style={{marginLeft:15}}>
<ListItem.Title>{item.title}</ListItem.Title>
<ListItem.Subtitle>{item.subtitle}</ListItem.Subtitle>
</ListItem.Content>
</>
}
isExpanded={isExpanded}
onPress={() => {
toggleAccordionItem(item.key);
}}
>
<ListItem item={item}>
<MaterialIcons.Button
name="airport-shuttle"
onPress={() => { alert(item.direction) }}>
<Text>Show Direction</Text>
</MaterialIcons.Button>
</ListItem>
</ListItem.Accordion>
);
};
return (
<SafeAreaView>
<SectionList
refreshing={!isRefreshing}
sections={sections}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
ItemSeparatorComponent={renderSeparator}
keyExtractor={(item) => item.key}
/>
</SafeAreaView>
)
Just started using React Native last week, but my workaround for a similar situation was to use the ListItem inside the accordion like this
{isExpanded && <ListItem item={item} />}
But it turns the animation of the accordion clunky. Don't know which is the correct way to address the issue, if someone has experiencied this issue before and know what is the proper way to solve i would appreciate
i have code for a working navigation bar, however i need to hide the navigation bar, due to having a video player to when clicked fullscreen mode. the navigation bar does not hide.
Navbar.js file here is the code for the navbar
export default class Navbar extends Component {
render() {
return(
<Header
style={{backgroundColor: Colors.navbarBackgroundColor}}
backgroundColor={Colors.navbarBackgroundColor}
androidStatusBarColor={Colors.statusBarColor}
noShadow={true}
>
{this.props.left ? this.props.left : <Left style={{flex: 1}} />}
<Body style={styles.body}>
<Title style={styles.title}>{this.props.title}</Title>
</Body>
{this.props.right ? this.props.right : <Right style={{flex: 1}} />}
</Header>
);
}
}
Here is the file where I am using the narbar, how can i hide ?
import Navbar from '../component/Navbar';
onFullScreen(fullScreen) {
}
return(
<SideMenuDrawer ref={(ref) => this._sideMenuDrawer = ref}>
//Hide this navbar
<Navbar left={left} right={right} title={this.props.title} />
<Container style={{backgroundColor: '#fdfdfd'}}>
<Video url={'http://techslides.com/demos/sample-videos/small.mp4'}
onFullScreen={status => this.onFullScreen(status)}
style={{ width: Dimensions.get('window').width, height: 200}}/>
</Container>
</SideMenuDrawer>
);
}
using your navbar is not a good idea always it's better to use native element's
but by the way you should have a callback for video play and stop
videoWillPlay = (event) => {
this.setState({flexSize: 0,Height:0});
};
videoWillStop = (event) => {
this.setState({flexSize: 3.5,Height:1});
};
then you can set navbar height zero or if it have flex zero the flex
and you should dynamic style
<Navbar left={left} right={right} title={this.props.title}
style={{flex: this.state.flexSize}}/>
you can also make video play to full size and don't touch navbar
You can conditionally render a component based on a boolean. So you could introduce a boolean you toggle based on if you're in fullscreen or not, then use it to decide if you want to render the nav bar:
{!isFullscreen && <Navbar left={left} right={right} title={this.props.title} />}
In reality that might be this.props.isFullscreen or this.state.isFullscreen depending on where you want to track the value, but that's the general concept.
Here's an example using internal state based on your current code:
export default class YourComponent extends Component {
constructor(props) {
super(props);
this.state = {
isFullScreen: false
};
}
onFullScreen = fullScreen => {
this.setState({
isFullScreen: fullScreen
});
}
render() {
const {isFullScreen} = this.state;
return (
<SideMenuDrawer ref={(ref) => this._sideMenuDrawer = ref}>
{!isFullScreen && <Navbar left={left} right={right} title={this.props.title} />}
<Container style={{ backgroundColor: '#fdfdfd' }}>
<Video
url={'http://techslides.com/demos/sample-videos/small.mp4'}
onFullScreen={this.onFullScreen}
style={{ width: Dimensions.get('window').width, height: 200 }} />
</Container>
</SideMenuDrawer>
);
}
}
I don't have all the info about your project, so this assumes the value passed back to Video's onFullScreen prop is a boolean. If it's an object, you might need to use something like this instead:
onFullScreen = status => {
this.setState({
isFullScreen: status.fullScreen
});
}