wix / react-native-ui-lib

UI Components Library for React Native
https://wix.github.io/react-native-ui-lib/
MIT License
6.43k stars 706 forks source link

Colors, Typography or Spacings return undefined in StyleSheet #1780

Open evg1n opened 2 years ago

evg1n commented 2 years ago

Description

Colors, Typography, Spacings cannot be used inStyleSheet.create() [Forked from #1513]

Related to

Steps to reproduce

Initialize any RN App. Put Colors.loadColors({...})in entry file where App Component is registered.

In any component file, use

const styles = StyleSheet.create({
  container: {
    backgroundColor: Colors.customColor //returns undefined
  }
})

Expected behavior

Any Colors property should return a HEX value. Example: Colors.customColor => "#374FE8"

Actual behavior

If you do not initialize StyleSheet.create() within the component (which is pointless) then Color values are not loaded properly. Returns undefined.

More Info

Can provide if requested.

Code snippet

// entry file index.js
import { registerRootComponent } from "expo";
import { Colors } from "react-native-ui-lib";
import App from "./App.jsx";

Colors.loadColors({
 customColor: "#374FE8"
});
registerRootComponent(App);
import { View, Text, Colors} from 'react-native-ui-lib'

const Home = (props) => {

// Will render a View with white background instead of 'customColor'
return (
  <View style={styles.container}> 
    <Text>Home</Text>
  </View>
)
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: Colors.customColor
  }
})

Screenshots/Video

No screenshots are available at this time.

Environment

// package.json
"dependencies": {
    "expo": "~44.0.0",
    "expo-splash-screen": "^0.14.1",
    "expo-status-bar": "~1.2.0",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-native": "0.64.3",
    "react-native-gesture-handler": "^2.1.1",
    "react-native-keyboard-aware-scroll-view": "^0.9.5",
    "react-native-reanimated": "^2.3.1",
    "react-native-ui-lib": "^6.7.1",
    "react-native-vector-icons": "^9.0.0",
    "react-native-web": "0.17.1",
    "react-redux": "^7.2.6",
    "react-router-dom": "^5.2.0",
    "react-router-native": "^5.2.0",
    "redux": "^4.1.2"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "redux-devtools-extension": "^2.13.9"
  },

Affected platforms

ethanshar commented 2 years ago

Hi @evg1n It's important to load your colors before importing any component file.

In your case, change the order of the code like this

// entry file index.js
import { registerRootComponent } from "expo";
import { Colors } from "react-native-ui-lib";

Colors.loadColors({
 customColor: "#374FE8"
});

import App from "./App.jsx";

registerRootComponent(App);
evg1n commented 2 years ago

Thank you for your response @ethanshar .

Unfortunately, your solution does not work for me. I moved import App from "./App.jsx"; after Colors.loadColors but no luck

Please observe the below screenshot. Container border color is black (gets color value from StyleSheet.create()) and text input is in primary color (gets color value from fieldStyle prop).

Screen Shot 2022-01-25 at 11 28 20
// Components
<View style={styles.formContainer}> // gets rendered in #000000
 <TextField
   onChangeText={handleUsernameChange}
   migrate
   clearButtonMode={"while-editing"}
   keyboardType={"email-address"}
   onFocus={() => setState({ ...state, focus: "username" })}
   onBlur={() => setState({ ...state, focus: "" })}
   fieldStyle={{
     borderColor:
     state.focus === "username"
       ? Colors.secondaryColor                   // gets rendered in my secondaryColor
       : Colors.primaryColor,                        // gets rendered in my primaryColor
     ...styles.textfield,
     }}
   label={USERNAME}
   floatingPlaceholder
   placeholder={USERNAME + " " + OR + " " + EMAIL}
   floatingPlaceholderColor={Colors.primaryColor} 
 />
</View>
const styles = StyleSheet.create({
  formContainer: {
    backgroundColor: "#ffffffdd",
    padding: 10,
    borderRadius: 10,
    borderWidth: 1,
    borderColor: Colors.primaryColor,
  },
  textfield: {
    borderWidth: 1,
    backgroundColor: "white",
    paddingTop: 15,
    paddingBottom: 10,
    paddingHorizontal: 10,
    borderRadius: 5,
  },
)
evg1n commented 2 years ago

It seems the best possible work-around is to combine styles inline.

style={{...styles, backgroundColor: Colors.customColor}}

vagnerlandio commented 2 years ago

I'm going through the same problem.

An interesting fact is that if I open the code editor and "save" the file, hot reloading react-native will make the style variables work in the StyleSheet.create({})

evg1n commented 2 years ago

I'm going through the same problem.

An interesting fact is that if I open the code editor and "save" the file, hot reloading react-native will make the style variables work in the StyleSheet.create({})

@vagnerlandio yes it works when hot reloading, but not on production unfortunately.

ethanshar commented 2 years ago

Hi, If anyone can share a link to small project that demonstrates the issue it will be really helpful with investigating the problem

LazyAfternoons commented 2 years ago

Hi, If anyone can share a link to small project that demonstrates the issue it will be really helpful with investigating the problem

I'm currently experiencing the same issue. You can find here a very simple example that demostrates the issue.

It's a simple full screen view with an orage background color but it you cold start the app it will be shown in white. As someone else already mentioned, any action which triggers hot-reloading will make the correct color appear.

RN: 0.69 (same issue on 0.68 and previous versions). RNUILib: 6.18.0 (same issue on previous versions). Device: anything I could test, Android 11, 12 and iPhone with iOS 15.

Thank you for your work.

evg1n commented 2 years ago

RN 0.70 is out, I don't know how this is affected.

runoob-coder commented 2 years ago

RN 0.70 is out, I don't know how this is affected.

I use in RN0.70 also reproduces this problem

runoob-coder commented 2 years ago

This is my solution:

@ethanshar @evg1n

import { View, Text, Colors} from 'react-native-ui-lib'

console.info('Home outside: ', Colors.customColor); // **undefined**

const Home = (props) => {

  console.info('Home inside: ', Colors.customColor); // **Yes! It work!**

  // **so,you should move the styles to here**
  const styles = StyleSheet.create({
    container: {
      backgroundColor: Colors.customColor
    }
  })

  return (
    <View style={styles.container}> 
      <Text>Home</Text>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: Colors.customColor // It can't work
  }
})
evg1n commented 2 years ago

Thank you for your contribution @runoob-coder, I don't currently have a use-case for this I already delivered the project. I will share my feedback when I work with RN next time.

However, the initial intent was to be able to separate style from the component itself. Because at some point want to be able to separate styles file from the component.

derscott commented 2 years ago

Hi,

I guess the problem is due to scopes in Javascript modules. When calling e.g. Colors.loadColors in a Javascript component file, the updated Colors object will only be available inside that very file. In all other component files, the imported Colors object won't contain any new or updated properties.

As a workaround, I wrote a small helper function which encapsulates all customization. Calling this function in every component file in module scope (i. e. outside of the actual component to be exported) fixed the issue for me. Of course this helper has to be called before accessing any of the updated properties inside StyleSheet.create() or using them as a default prop to take effect.

This does not only apply to Colors, but also to Spacings and Typography.

@ethanshar @runoob-coder @evg1n

BLOCKMATERIAL commented 1 year ago

same issue

ozdemiremrah commented 1 year ago

I spent hours trying to figure it out. I haven't fully figured it out.

This problem occurs when I want to use rnu foundation in StyleSheet. For now, I have to use a helper function for each component.

However, as I approach it logically, there is no situation where the foundation needs to feed the StyleSheet, so this may not be a problem.

vagnerlandio commented 1 year ago

With me the problem is not in the StyleSheet. It doesn't work even when I use the Colors, Spacings and Typography without StyleSheet and outside the component scope.

// It return undefined for Colors.primary and Spacings.s1
const MyComponent = () => <View height={height} style={$container} />

const $container = { backgroundColor: Colors.primary }
const height = Spacings.s1

For it to work I must refactor the code and move the styles to the component's scope.

// It work
const MyComponent = () => {
  const $container = { backgroundColor: Colors.primary }
  const height = Spacings.s1

  return <View height={height} style={$container} />
}
ethanshar commented 11 months ago

It's all a matter of a race-condition. If you loadColors before requiring (importing) the components files they will use the updated colors you have loaded.

Try that Add a log to a global scope of a component and another log right before you call Colors.loadColors If the component log is being called before the loadColors log then you have a problem and you import you components files too soon, or loading the custom colors too later.

kitolog commented 1 month ago

It works for me with the bare RN project, but it doesn't work when you connect Expo. For me, the behaviour is the same as @runoob-coder described.

TungXiao commented 2 weeks ago

in my project we fixed it by change code style

// theme/index.ts

const RNUIInit = () => {
  RNUIColor.loadColors({
    primary_color: "#F47920"
  });
};

// init theme in this file otherwise theme will not work in Stylesheet.create function
RNUIInit();

export { RNUIInit }
//  index.ts

import './theme';
import App from './App';

AppRegistry.registerComponent(appName, () => App); // it's work
//  index.ts

import { RNUIInit }'./theme';
RNUIInit();
import App from './App';

AppRegistry.registerComponent(appName, () => App); // it's not work

i don't know is it work for your project, but work for me.