Passing props into external stylesheet in React Native?
Asked Answered
T

10

44

I'm new to React and React Native. At the moment for each component I'm breaking the code into 2 separate files:

  1. index.js for all the React code, and;
  2. styles.js for the StyleSheet

Is there a way to pass props into the external StyleSheet?

Example: index.js:

render() {
  const iconColor = this.props.color || '#000';
  const iconSize = this.props.size || 25;

  return (
    <Icon style={styles.icon} />
  );
}

Example styles.js:

const styles = StyleSheet.create({
  icon : {
    color: iconColor,
    fontSize: iconSize
  }
});

The code above does not work, but it's more there just to get the point across of what I'm trying to do. Any help is much appreciated!

Tereus answered 9/3, 2017 at 23:14 Comment(0)
A
19

Create a class that takes iconColor and iconSize as arguments and returns a StyleSheet object

// styles.js

export default class StyleSheetFactory {
    static getSheet(iconSize, iconColor) {
        return StyleSheet.create({
            icon : {
                color: iconColor,
                fontSize: iconSize
            }
        })
    }
}

// index.js

render() {
    let myStyleSheet = StyleSheetFactory.getSheet(64, 'red')
}
Aronson answered 9/3, 2017 at 23:39 Comment(4)
That does indeed work, so thank you. I replaced the static parameters you're passing in with the props to make it more dynamic. However, I'm not certain it's the most elegant solution. What if I have more than 2? I'm also not 100% certain splitting up the styles and the react code is the best, I'm just trying to learn as much as I can. Thanks again!Tereus
@Tereus I personally prefer not splitting up styles because I find it makes the code more readable and prefer making additional components with inline styles vs having a huge style sheet where I have to lookup a style - in the end it comes down to preference but there are benefits to having separate sheets see #39336766 -Aronson
why to create class field when it can directly pass to stylesheetPuissance
why one would like to prefer classes in the era of functional componentWeen
D
69

I rather to have my styles in a separate file styles.js. Inside styles.js:

export const styles = (props) => StyleSheet.create({
        icon : {
        color: props.iconColor,
        fontSize: props.iconSize
      }
    }

Inside your main class you can pass the value

return (
    <Icon style={styles(this.props).icon} />
  );

Alternatively you can those value directly so it would be

export const styles = (iconColor,iconSize) => StyleSheet.create({
    icon : {
    color: iconColor,
    fontSize: iconSize
  }
}

and inside your main class

return (
    <Icon style={styles(this.props,iconColor, 
this.props.iconSize).icon} />
 );
Danie answered 1/7, 2018 at 12:51 Comment(3)
Are there any performance drawbacks by using this method because this will initiate an arrow function ?Acclimate
@JohnStuart The "Performance drawback" is the exact same a typical inline style: A new style object will be created every time the component renders, since stylesheet.create is being called every time the component renders. In practice is it very unlikely that it will matter even a little bit, though.Goaltender
Replying to @JohnStuart, instead of creating the arrow function for the whole styles object, we can keep it as it is and create separate arrow functions for those styles which needs to change dynamically. Those functions will be small and their change will not affect the main styles object.Calcutta
M
33

i'm sending noFooter boolean prop in a style sheet

   <View style={styles.mainFooterCont(noFooter)}>
     <Text> Testing </Text>
    </View>

and receiving it like

  mainFooterCont: noFooter => ({
   flexDirection: 'row',
   justifyContent: 'space-between',
   alignItems: 'flex-end',
   paddingBottom: noFooter ? 0 : 20,
   paddingTop: Metrics.ratio(noFooter ? 0 : 5),
   }),
Myosin answered 15/5, 2020 at 12:19 Comment(4)
This does seem to work but typescript will complain.Odyssey
@DomagojVuković no idea for typescriptMyosin
This is by far the best and cleanest answer here. Only the styles that need props have to deal with them.Erek
Anyone managed to get this done without Typescript complaining?Shall
A
19

Create a class that takes iconColor and iconSize as arguments and returns a StyleSheet object

// styles.js

export default class StyleSheetFactory {
    static getSheet(iconSize, iconColor) {
        return StyleSheet.create({
            icon : {
                color: iconColor,
                fontSize: iconSize
            }
        })
    }
}

// index.js

render() {
    let myStyleSheet = StyleSheetFactory.getSheet(64, 'red')
}
Aronson answered 9/3, 2017 at 23:39 Comment(4)
That does indeed work, so thank you. I replaced the static parameters you're passing in with the props to make it more dynamic. However, I'm not certain it's the most elegant solution. What if I have more than 2? I'm also not 100% certain splitting up the styles and the react code is the best, I'm just trying to learn as much as I can. Thanks again!Tereus
@Tereus I personally prefer not splitting up styles because I find it makes the code more readable and prefer making additional components with inline styles vs having a huge style sheet where I have to lookup a style - in the end it comes down to preference but there are benefits to having separate sheets see #39336766 -Aronson
why to create class field when it can directly pass to stylesheetPuissance
why one would like to prefer classes in the era of functional componentWeen
S
16

Just wrap stylesheet in a function where you can optionally pass props.

Instead of:

const styles = StyleSheet.create({
  Title: { color: 'white' }
});

You do:

const styles = (props?: any) => StyleSheet.create({
  Title: { color: 'white' }
});

And now when you add them to your components, instead of

style={styles.Title}

You do:

style={styles(propsObjectHere).Title}

and since this is optional and you have no props to pass, just do:

style={styles().Title}

P.S. ignore the type if you, for some reason, are not using TypeScript :P

Spragens answered 6/12, 2020 at 14:44 Comment(2)
TBH, reflecting on this after using it, I find it quite messy and creates unnecessary dependencies in your style. What I ended up doing is create static styles and just conditionally change styles at component level through the styles attribute. <Component style={ StyleSheet.flatten([MainStyle, props.darkMode ? {color: '#333'} : {}]) } /> <== untested codeSpragens
better put all dynamic styles into one function, generate static then render, otherwise it gets called too many timesCyprio
W
3

If you do not prefer to create a class then simply just create a function followed by a key and return an object from the function, you just have to pass the parameter by which you want to evaluate the condition. here is the exmple

export const Style = StyleSheet.create({
 locatorTextInputContainer: locatorType => ({
        flexDirection: 'row',
        backgroundColor: locatorType == 'None' || locatorType == '' ? GColors.separatorColor : GColors.white,
        marginBottom: 10,
        paddingBottom: 5,
        marginStart: 10,
        marginEnd: 10,
   })
})

and you can use it as follows

 <View style={Style.locatorTextInputContainer(locatorType)}>
    <TextInput
        value={sourceLocator}
        onChangeText={(text) => {
            dispatch(setSourceLocator(text))
        }}/>
 </View>
Ween answered 8/2, 2022 at 13:53 Comment(1)
This doesn't work with TypescriptRevulsion
N
2

Here is a simpler solution.

Component

 <View
    style={{
      ...styles?.tabItem_bottomView,
      backgroundColor: selected ? Color?.blue : Color?.white,
    }}
  />

You can just use the stylesheet as before. Nothing to edit there.

Nyala answered 5/1, 2022 at 6:55 Comment(5)
It's better for performance to use: style={[styles.tabItem_bottomView, { backgroundColor: selected ? Color.blue : Color.white ]}Muttonhead
Why? Can you explain a bit?Nyala
I can't remember where I read it now, but there was some doc somewhere that mentioned favouring style arrays to object spreading. I think spreading is slightly costlier on re-renders, but take it with a grain of salt because I can't remember the source, sorry!Muttonhead
No worries, Thank you for the info. I'll see if i can find anything regarding thisNyala
Cheers, likewise if I come across the doc again I'll post here it.Muttonhead
R
2

an alternative to @Belsim Jarosh's answer that we implemented is having the theme integrated with the styles file and use the params to send other props like width, height, etc from Dimensions.get('window') like this:

import { StyleSheet } from 'react-native';
import { theme } from '../path/to/theme.ts'

type Props = {
  width: any;
}

const getStyles = (props: Props) =>
  StyleSheet.create({
    container: {
      backgroundColor: theme.backgroundVariant,
      width: props.width * 0.7;
  },
})

so we assign getStyles to a constant:

const styles = getStyles(theme);

sample of implemented code:

...
import { Dimensions, View } from 'react-native';
import { getStyles } from '../path/to/getStyles.ts';
  
const Register = () => {
  const { width } = Dimensions.get('window');
  const styles = getStyles({ width });

  return <View style={styles.container}></View>;
};
Revulsion answered 28/7, 2023 at 5:15 Comment(0)
B
1

Solution:

render() {

   const iconColor = this.props.color || '#000';
   const iconSize = this.props.size || 25;

   return (
   <Icon style={{...styles.icon, color: iconColor, fontSize: iconSize }} />

Example styles.js:

  const styles = StyleSheet.create({
  icon : {
    color: iconColor,
    fontSize: iconSize
  }})
Boyce answered 29/7, 2021 at 0:53 Comment(1)
iconColor and iconSize will only be recognized if styles are declared inside the component and after those variables, that's why the OP is asking to pass them as propsRevulsion
H
1

Instead of passing the props to the StyleSheet, we can create the stylesheet a function and then call it in the main function. then we can use that variable for use in the function. We won't even get the TypeScript Errors.

const getStyles = (theme: any) =>
  StyleSheet.create({
    container: {
      backgroundColor: theme.backgroundVariant,
    },
 })

Now after creating the function we can call the function in the main function like the below one

const styles = getStyles(theme);

The Below is a sample of how you can implement the code

const Register = memo((_props: RegisterProps) => {
  const theme = useSelector(state => state.theme);
  const styles = getStyles(theme);

  return <View style={styles.container}></View>;
});
Hypogeum answered 3/4, 2023 at 11:6 Comment(0)
W
0

For those using Typescript:

The answer provided here is excellent.

Well said by the writer:

StyleSheet is like a static stuff! You are not supposed to pass argument to it! All you should do is using an array to pass style.

Hence, we do:

<View
    style={[
        styles.tabItem_bottomView,
        { backgroundColor: selected ? Color.blue : Color.white }
    ]}
/>
Watering answered 27/7, 2023 at 4:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.