Insert Dynamic Variable Into React Native Stylesheet - javascript

I have a react native view i want to style dynamically.
The value of reaction will be sourced from an API, so i want to pass it into my styleheet
const Weather = ({ reaction, temperature }) => {
//const bg = `weatherconditions.${reaction}.color`
return (
<View
style={{ backgroundColor: weatherConditions[reaction].color }}>
The stylesheet looks like this
export const weatherConditions = {
Rain: {
color: '#005BEA',
title: 'Raining',
subtitle: 'Get a cup of coffee',
icon: 'weather-rainy'
},
Clear: {
color: '#f7b733',
title: 'So Sunny',
subtitle: 'It is hurting my eyes',
icon: 'weather-sunny'
},
Thunderstorm: {
color: '#616161',
title: 'A Storm is coming',
subtitle: 'Because Gods are angry',
icon: 'weather-lightning'
},
Clouds: {
color: '#1F1C2C',
title: 'Clouds',
subtitle: 'Everywhere',
icon: 'weather-cloudy'
},
Snow: {
color: '#00d2ff',
title: 'Snow',
subtitle: 'Get out and build a snowman for me',
icon: 'weather-snowy'
},
}
where either Clear, Rain, ThunderStorm can be the value of reaction
I want to dynamically provide the reaction value.
i have tried to do this
const Weather = ({ reaction, temperature }) => {
const bg = `weatherconditions.${reaction}.color`;
return (
<View
style={{ backgroundColor: bg }}
>
and
<View
style={{ backgroundColor: ${bg }}>
But none of them seem to work.
Any help solving this will be appreciated.

Not sure this is what you mean but hope it helps.
const styles = {
weather = reaction => ({
backgroundColor: reaction
})
}
And then in your <View/> tag provide the reaction
...
<View style={StyleSheet.flatten([styles.weather(reaction)])}>
//Your code here
</View>

Related

React Native - Changing the indicator width according to tab bar text width (react-native-tab-view)

I am using react-native-tab-view, and trying to change the indicator width. I would like indicator width to be the same with the tab text. But What I did is just the default. I have tried in many ways, but always it gave me the wrong result. The tab bar should be scrollable horizontally as well. Could you check which part I should change?
This is the expected result :
ShowAllIndex Code :
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.dark,
},
});
const ShowAllIndex = () => {
const { seller } = useSelector((s) => s.auth, shallowEqual);
const [routes] = useState([
{ key: 'best', title: 'BEST' },
{ key: 'jacket', title: '아우터' },
{ key: 'pants', title: '바지' },
{ key: 'skirts', title: '스커트' },
{ key: 'topClothe', title: '원피스' },
{ key: 'one', title: '바지' },
{ key: 'two', title: '스커트' },
{ key: 'three', title: '상의' },
]);
const renderScene = SceneMap({
best: ShowAllMainRoutes,
jacket: JacketRoutes,
pants: PantsRoutes,
skirts: SkirtsRoutes,
topClothe: TopClotheRoutes,
one: ShowAllMainRoutes,
two: JacketRoutes,
three: PantsRoutes,
});
return (
<ScrollView style={[styles.container, { marginTop: Global() }]}>
<CustomTabView
routes={routes}
renderScene={renderScene}
scrollEnabled={true}
tabStyle={{ width: 'auto' }}
showAll={true}
/>
</ScrollView>
);
};
export default ShowAllIndex;
CustomTabView code :
const initialLayout = { width: Dimensions.get('window').width };
const CustomTabView = ({
routes,
renderScene,
numberOfTabs,
indicatorWidth,
scrollEnabled = false,
tabStyle,
showAll,
indicatorStyle,
}) => {
const [index, setIndex] = useState(0);
const renderTabBar = (props) => (
<TabBar
{...props}
scrollEnabled={scrollEnabled}
indicatorStyle={[
indicatorStyle,
{
backgroundColor: colors.barbie_pink,
height: 2.5,
bottom: -1,
},
]}
style={[styles.tabBar]}
renderLabel={({ route, focused }) => {
return (
<Text
style={[styles.label, focused ? styles.activeLabel : styles.label]}
>
{route.title}
</Text>
);
}}
tabStyle={tabStyle}
/>
);
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
renderTabBar={renderTabBar}
onIndexChange={setIndex}
initialLayout={initialLayout}
style={[styles.container]}
/>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.dark,
},
scene: {
flex: 1,
marginTop: 5,
},
tabBar: {
backgroundColor: 'transparent',
shadowOpacity: 0,
elevation: 0,
borderBottomWidth: 0.5,
borderColor: colors.very_light_pink_two,
marginBottom: 5,
},
label: {
color: colors.very_light_pink_four,
fontSize: 14,
lineHeight: 20.8,
fontFamily: 'NotoSansCJKkr-Regular',
letterSpacing: -0.35,
},
activeLabel: {
color: colors.barbie_pink,
},
});
Thank you for answers!
Check this solution:
const TAB_MARGIN = 24;
<TabBar
...
scrollEnabled
renderIndicator={indicatorProps => {
const width = indicatorProps.getTabWidth(this.state.index) - TAB_MARGIN
return <TabBarIndicator {...indicatorProps} width={width} />
}}
indicatorStyle={{
backgroundColor: '#333',
height: 4,
left: TAB_MARGIN / 2,
}}
...
/>
I think the prop indicatorStyle in TabBar that can resolves you problem.
You can do the following:
<TabBar
scrollEnabled //add this line to make it scrollable
tabStyle={{width: 100}} //and this one to change the tab width
/>

How to highlight a single button in react native?

How can I highlight a single button surrounded by multiple buttons?
Here is my component that renders the button. Also, I imported a CustomButton that I created a while ago.
const button = [
{
title: '#Food',
selected: false,
id: 1
},
{
title: '#Fashion',
selected: false,
id: 2
},
{
title: '#Art',
selected: false,
id: 3
}]
{button.map(({ title, id, selected }) => {
return (
<View style={{ width: '25%', padding: 5, }}>
<CustomButton
bgColor={active ? 'red' : 'blue'}
title={title}
key={id}
onPress={() => chosenButton(selected, id)}
textColor={Colors.PRIMARY_COLOR} />
</View>
)
})}
Here is my CustomButton
const CustomButton = ({ title, containerStyle, textStyle, bgColor, textColor, onPress }) => {
return (
<Button onPress={onPress} block rounded style={[styles.btnStyle, containerStyle, { backgroundColor: bgColor, }]}>
<Text style={[styles.text, textStyle, { color: textColor }]}>{title}</Text>
</Button>
);
};
As of now, here is my buttons
But I want to highlight a single button and change the background color of it if clicked. How can I do that?
You could do useState in child component and change the colour on click
const CustomButton = ({ title, containerStyle, textStyle, bgColor, textColor, onPress }) => {
const [bgCol,setBgCol] = useState(bgColor);
const changeBg = () => setBgCol('yellow');
return (
<Button onPress={()=>{changeBg();onPress()}} block rounded style={[styles.btnStyle, containerStyle, { backgroundColor: bgCol, }]}>
<Text style={[styles.text, textStyle, { color: textColor }]}>{title}</Text>
</Button>
);
};
In order to persist the selected value in the buttons array, you can do like
const [button, setButton] = useState([
{
title: '#Food',
selected: false,
id: 1
},
{
title: '#Fashion',
selected: false,
id: 2
},
{
title: '#Art',
selected: false,
id: 3
}]);
const handleButtonClick = (index) => {
const newData = [...button];
newData[index].selected = !newData[index].selected;
setButton(newData);
}
{button.map(({ title, id, selected }, index) => {
return (
<View style={{ width: '25%', padding: 5, }}>
<CustomButton
bgColor={selected ? 'red' : 'blue'}
....
onPress={() => handleButtonClick(index)}
/>
</View>
)
})}

React material-table: Color rows in data-tree

I am using material-table in my React project. I have 3 levels of the data tree. Here it is the first one:
Is it possible when I click the first of the 2 items on 1st Level in data tree table to color it so it would be easier to see that values under it are child elements. Like this:
Also I would be even happier if it is possible to color it when I am passing data to it. Here it is how I am passing data:
data={[
{
id: 1, // MAIN ELEMENT
name: "Parent",
value: `Parent`,
},
{
id: 2, //CHILD OF THE MAIN ELEMENT
name: "Child",
value: `Child`,
parentId: 1,
}]}
Is there an option to color parent Element even before opening it, so it would be clear that it is PARENT and other is CHILD?
UPDATE:
Here is codesandbox example. As you can see when you open Parent1 Parent2 seems to be under Parent1. I want to make it clear that it is NOT under it.
https://codesandbox.io/s/jolly-germain-6fncr?file=/src/App.js
Let we talk about this problem first. It's neither programmatic nor css problem. It's just the problem how you show data, in other words, UX only.
There are several ways to achive, this is my work example: https://codesandbox.io/s/withered-dust-hb882?file=/src/App.js
Basically I just add one first column for parent only, that's it.
Ok, using CSS selectors it is not so easy to implement onExapnd color change. Here you will have to write check for parent TR check and sub Button rotate(90deg) check. To change the colors without onClick check you can use the following CSS:
tr[level="0"] {
background-color: #FF0000;
}
tr[level="1"] {
background-color: #FF0033;
}
tr[level="2"] {
background-color: #FF0066;
}
In JS way you can use the following code (of course you will have to add it in every table or extend the table or use util lib with ready rowStyle method..)
import React from "react";
import MaterialTable from "material-table";
import SearchIcon from "#material-ui/icons/Search";
import RotateLeftIcon from "#material-ui/icons/RotateLeft";
import { ArrowUpward, ChevronRight } from "#material-ui/icons";
//import './styles.css';
export default () => {
const constPathColors = {
1: '#FFFF00',
2: '#FFFF33',
3: '#FFFF66',
4: '#FFFF99',
5: '#FFFFCC'
};
return (
<MaterialTable
style={{ width: "100%", margin: "3%" }}
title="Income Statement"
icons={{
Filter: React.forwardRef((props, ref) => <SearchIcon ref={ref} />),
Search: React.forwardRef((props, ref) => <SearchIcon ref={ref} />),
ResetSearch: React.forwardRef((props, ref) => (
<RotateLeftIcon ref={ref} />
)),
SortArrow: ArrowUpward,
DetailPanel: ChevronRight
}}
columns={[
{
field: "name",
title: "Category"
},
{
field: "value",
title: "Value",
cellStyle: {
textAlign: "center"
}
}
]}
data={[
{
id: 1, // MAIN ELEMENT
name: "Parent 1",
value: `SomeParentValue`
},
{
id: 2, //CHILD OF THE MAIN ELEMENT
name: "Child 1-1",
value: `Child Value`,
parentId: 1
},
{
id: 3, //CHILD OF THE MAIN ELEMENT
name: "Child 1-2",
value: `Child Value`,
parentId: 1
},
{
id: 4, //CHILD OF THE CHILD ELEMENT
name: "Child 1-2-1",
value: `Child Value`,
parentId: 3
},
{
id: 5, // MAIN ELEMENT
name: "Parent 2",
value: `SomeParentValue`
}
]}
parentChildData={(row, rows) => rows.find(a => a.id === row.parentId)}
options={{
paging: false,
headerStyle: {
backgroundColor: "#378FC3",
color: "#FFF",
fontSize: "17px",
textAlign: "center",
fontWeight: "bold"
},
rowStyle: rowData => {
if(rowData.tableData.isTreeExpanded === false && rowData.tableData.path.length === 1) {
return {};
}
const rowBackgroundColor = constPathColors[rowData.tableData.path.length];
return {backgroundColor: rowBackgroundColor};
}
}}
/>
);
};
The row has default color before the expanding:
After expanding it has yellow (gradient depend on level) background color:
Thats how my tree view looks like. Thanks to the left: `var(--left-before, ${0}px), i could positioning the :befores wherever i want
https://i.ibb.co/Wp9XJcc/childscapture.png
viewTableTree.styles.js
import { makeStyles } from '#material-ui/core/styles';
export const useViewTableTreeStyles = makeStyles(theme => ({
root: {
'& .MuiPaper-root': {
boxShadow: 'none'
},
'& .MuiTable-root': {
position: 'relative',
overflow: 'hidden'
},
'& .MuiTableRow-root': {
'&:hover': { backgroundColor: 'rgba(0, 0, 0, 0.04)' },
'&:before': {
content: '""',
fontWeight: theme.font.weight.black,
fontSize: theme.font.size.xxl,
position: 'absolute',
left: `var(--left-before, ${0}px)`, //important trick here!
width: '1px',
height: '3.2rem',
backgroundColor: theme.palette.basic.bright
}
}
}
}));
then in the MaterialTable component
ViewTableTree.js
<div className={classes.root}>
<MaterialTable
icons={tableIcons}
data={rows}
columns={cells}
localization={{
header: {
actions: ''
}
}}
options={{
selection: false,
paging: false,
search: false,
showTitle: false,
toolbar: false,
actionsColumnIndex: -1,
rowStyle: rowData => {
let styles = { transition: 'transform 300ms' };
const levels = rowData.tableData.path.length === 1 ? 0 : rowData.tableData.path.length;
styles = { ...styles, '--left-before': `${levels * 6}px` };
return rowData.tableData.isTreeExpanded
? {
...styles,
fontWeight: 600,
backgroundColor: 'rgba(77, 93, 241, 0.08)'
}
: styles;
}
}}
{...props}
/>
</div>

Detect click on header icons

I am very new with react native. I am using a header from react-native-elements in my app. This is my code
<Header
leftComponent={{ icon: 'menu', color: '#fff' }}
centerComponent={{ text: 'MY TITLE', style: { color: '#fff' } }}
rightComponent={{ icon: 'home', color: '#fff' }}
/>
I was not able to found any example on how to detect the clicks on the icons and if possible show an extra view. My goal is to achieve something like this :
Do you have any example or sample code?
Write : insted of = , onPress will work
<Header
leftComponent={ {text:'ok',color: '#fff', onPress: () => this.myFunction()}}
centerComponent={{ text: 'MY TITLE', style: { color: '#fff' } }}
/>
or you can pass your own component like
leftComponent={<MyCustomLeftComponent />}
You have to use onPress instead of onClick.
<Header
leftComponent={{ icon: 'menu', color: '#fff' }}
centerComponent={{ text: 'MY TITLE', style: { color: '#fff' } }}
rightComponent={{ icon: 'home', color: '#fff' onPress={() => this.yourFunction} }}
/>

Fetching JSON and iterating through the values in react-native

I'm new to react native, and I'm trying to simply iterate through a sample json file but am receiving the error undefined is not a function (evaluating 'this.state.results.map')
I have set the state initially to be an object, so not sure why i am receiving this error.
Here is the JS:
import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View, StyleSheet, TouchableHighlight } from 'react-native';
var REQUEST_URL = 'https://facebook.github.io/react-native/movies.json';
class WPReact extends Component {
constructor(props) {
super(props);
this.state = {results: []};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
results : { responseData }
});
})
.done();
}
render() {
return this.renderJSON();
}
renderJSON() {
contents = this.state.results.map((item) => {
<View key={item.movies.title} style={ styles.container }>
<Text style={styles.title}>
{item.movies.title}
</Text>
</View>
});
return (
<View style={styles.container}>
{contents}
</View>
);
}
}
var Dimensions = require('Dimensions');
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF',
},
textContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 30,
textAlign: 'center',
margin: 10,
},
text: {
fontSize: 18,
paddingLeft: 20,
paddingRight: 20,
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => WPReact);
EDIT
So i have edited the renderJSON() to and also removed the braces of responseData as you said, as it was already an object:
renderJSON() {
console.log(this.state.results.description);
contents = this.state.results.movies.map((item) => {
<View key={item.title} style={ styles.container }>
<Text style={styles.title}>
{item.title}
</Text>
</View>
});
return (
<View style={styles.container}>
{contents}
</View>
);
}
I added a console log to see if i can output some of the data, and i can see the description. The sample JSON i am using is (demo from react):
{
"title": "The Basics - Networking",
"description": "Your app fetched this from a remote endpoint!",
"movies": [
{ "title": "Star Wars", "releaseYear": "1977"},
{ "title": "Back to the Future", "releaseYear": "1985"},
{ "title": "The Matrix", "releaseYear": "1999"},
{ "title": "Inception", "releaseYear": "2010"},
{ "title": "Interstellar", "releaseYear": "2014"}
]
}
I can log the description and title. But I am still receiving: ReactNativeJS: undefined is not an object (evaluating 'this.state.results.movies.map')
And if I try logging console.log(this.state.results.movies[0].title) I am receiving undefined is not an object (evaluating 'this.state.results.movies[0]')
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
console.log(responseData);
this.setState({
results : responseData
});
})
.done();
}
console.log(responseData) shows:
03-29 13:49:53.028 3062 4143 I ReactNativeJS: { title: 'The Basics - Networking',
03-29 13:49:53.028 3062 4143 I ReactNativeJS: description: 'Your app fetched this from a remote endpoint!',
03-29 13:49:53.028 3062 4143 I ReactNativeJS: movies:
03-29 13:49:53.028 3062 4143 I ReactNativeJS: [ { title: 'Star Wars', releaseYear: '1977' },
03-29 13:49:53.028 3062 4143 I ReactNativeJS: { title: 'Back to the Future', releaseYear: '1985' },
03-29 13:49:53.028 3062 4143 I ReactNativeJS: { title: 'The Matrix', releaseYear: '1999' },
03-29 13:49:53.028 3062 4143 I ReactNativeJS: { title: 'Inception', releaseYear: '2010' },
03-29 13:49:53.028 3062 4143 I ReactNativeJS: { title: 'Interstellar', releaseYear: '2014' } ] }
console.log(this.state.results.movies);
03-29 14:18:05.483 3062 4726 I ReactNativeJS: undefined
03-29 14:18:05.510 3062 4726 I ReactNativeJS: [ { title: 'Star Wars', releaseYear: '1977' },
03-29 14:18:05.510 3062 4726 I ReactNativeJS: { title: 'Back to the Future', releaseYear: '1985' },
03-29 14:18:05.510 3062 4726 I ReactNativeJS: { title: 'The Matrix', releaseYear: '1999' },
03-29 14:18:05.510 3062 4726 I ReactNativeJS: { title: 'Inception', releaseYear: '2010' },
03-29 14:18:05.510 3062 4726 I ReactNativeJS: { title: 'Interstellar', releaseYear: '2014' } ]
I see a couple of things you need to change.
Firstly, you need to bind fetchData method when you are using ES6 doing this this.fetchData = this.fetchData.bind(this); in the constructor (look for other ways to do this).
Secondly, map should be applied to this.state.results.movies due this is the array (following your post). this.state.results is not an array, is an object containing an array.
import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View, StyleSheet, TouchableHighlight } from 'react-native';
var REQUEST_URL = 'https://facebook.github.io/react-native/movies.json';
class WPReact extends Component {
constructor(props) {
super(props);
this.state = {
//Lets initialize results with the same struct we expect to receive from the api
results: {
title: '',
description: '',
movies: []
}
};
//Using ES6 we need to bind methods to access 'this'
this.fetchData = this.fetchData.bind(this);
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
results: responseData
});
})
.done();
}
render() {
//this.state.results.movies is the array you have to iterate
contents = this.state.results.movies.map((item) => {
//We need to return the corresponding mapping for each item too.
return (
<View key={item.title} style={ styles.container }>
<Text style={styles.title}>
{item.title}
</Text>
</View>
);
});
return (
<View style={styles.container}>
{contents}
</View>
);
}
}
var Dimensions = require('Dimensions');
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF',
},
textContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 30,
textAlign: 'center',
margin: 10,
},
text: {
fontSize: 18,
paddingLeft: 20,
paddingRight: 20,
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => WPReact);
Let me know if its works, I havent tested yet but I will soon.
React components using ES6 classes don't autobind this to non React methods. In your constructor, add:
this.renderJSON = this.renderJSON.bind(this)
responseData has to be an array for the Array#map() method to be available as a method on it.
You are setting results to an object containing the value of your responseData:
this.setState({
results : { responseData } // Object literal notation albeit possibly incorrect depending on the value of responseData
});
Remove surrounding braces if you are sure responseData is an array:
this.setState({
results : JSON.parse(responseData);
// You'll want to parse your JSON data here too :)
// If responseData is an array, you'll be able to call .map on it
});
// this.state.results.movies.map should now work
// (given the structure of the expected JSON)

Categories