I want to use backgroundColor of style1 as a state, and change it in the function change().
How can I access style1?
My point is to call the function change from another function, making the button change its color to yellow, then change it's colour to blue again after sometime.
export default class App extends Component{
constructor (props){
super(props);
this.state = {
//style1.backgroundColour: blue //? Can't
}
this.change=this.change.bind(this)
}
change() {
this.setState({ style1.backgroundColour: yellow }) //?
}
render(){
return (
<View style={styles.style1} > </View>
);
}
}
const styles = StyleSheet.create({
style1:{
padding: 5,
height: 80,
width: 80,
borderRadius:160,
backgroundColor:'blue',
},
});
Update: This question and my answer was based on class components. But functional components and hooks are the current way of using React for a long time now.
First you should create a state for your backgroundColor:
const [backgroundColor, setBackgroundColor] = useState('blue');
then change it's value in your function
setBackgroundColor('yellow');
and finally, use it in style prop:
style={[styles.style1, {backgroundColor}}
Old answer, using class components
You can give an array to style prop. So you can put any other styles from another sources.
First you should declare a default value for your backgroundColor state:
this.state = {
backgroundColor: 'blue',
};
then set it's state in your function as normal string state:
this.setState({backgroundColor: 'yellow'});
and finally, use it in style prop:
style={[styles.style1, {backgroundColor: this.state.backgroundColor}]}
Related
I have a functional react component with a style defined as below:
const StyledInnerItem = withStyles((theme) => ({
bgColor: {
backgroundColor: (props) => {
console.log('styledInnerItem color: ', props.color);
return 'red';
}
}
}))(InnerItem);
Basically I am trying to pass the props.color as the background color, however it's not even returning red which is set temporarily as the bg Color.
When I log the props, it is returning the color from the first render, and it is adding in the html file. but the styles only get added when I click on my component and the colors get applied to the component.
It works fine when the background Color is not a function and I need it to work the same by reading the color from the props.
The issue might be because the withStyles HOC only updates the styles when the component re-renders, but the props passed to the component do not trigger a re-render. One way to solve this is to pass the color value to the component as a state and update the state when the props change, which will trigger a re-render and the styles will get updated.
Here's how you can implement this:
const StyledInnerItem = withStyles((theme) => ({
bgColor: {
backgroundColor: (props) => props.color
}
}))(InnerItem);
class InnerItemWrapper extends React.Component {
state = {
color: 'red'
};
componentDidUpdate(prevProps) {
if (this.props.color !== prevProps.color) {
this.setState({ color: this.props.color });
}
}
render() {
return <StyledInnerItem color={this.state.color} />;
}
}
In this way, when the props.color changes, the component re-renders and the styles get updated accordingly.
Some Background:
Currently I'm trying to implement a TouchableOpacity component that represents a switch, which has a state of either 'on' or 'off'. The component works as follows:
On load, the component reads using AsyncStorage using the current date as the key (if an entry exists for the key, then the component has the state 'on' (true), if not 'off' (false)
The background color of the child View component is related to the state ('on': backgroundColor is green; 'off': backgroundColor is 'none')
If the component is pressed, the component is disabled while waiting for the AsyncStorage method to complete.
If the component is pressed while the state is 'off', a new entry is written, and on completion, the state is set to 'on'.
If the component is pressed while the state is 'on', the entry with the given key is deleted, and the state is set to 'off'.
Current Code:
class Switch extends Component {
constructor(props) {
super(props);
this.state = {
switchStatus: null,
awaitingIO: false
};
}
componentDidMount() {
switchStatusOfDate(new Date()).then((response) => {
this.setState({ switchStatus: response != null ? true : false });
}).catch((error) => {
// error
});
}
render() {
const {switchStatus, awaitingIO } = this.state;
if (switchStatus == null) {
return <View style={[styles.circleButton]}>
<Text>Loading</Text>
</View>
}
const style = {
backgroundColor: switchStatus ? 'green' : 'none',
...styles.circleButton
};
return (<TouchableOpacity disabled={awaitingIO} onPress={this._updateSwitchStatus.bind(this)} >
<View style={style}>
{/* Some children */}
</View>
</TouchableOpacity>);
}
async _updateSwitchStatus() {
this.setState({ awaitingIO: true });
if (this.state.switchStatus) {
await unsetSwitchStatusForDate(new Date());
} else {
await setSwitchStatusForDate(new Date());
}
this.setState({
switchStatus: !this.state.switchStatus,
awaitingIO: false,
});
}
}
const styles = StyleSheet.create({
circleButton: {
flex: 0,
alignItems: 'center',
justifyContent: 'center',
width: 150,
height: 150,
borderWidth: 2,
borderRadius: 75,
borderColor: 'green'
},
});
export default Switch;
Storage Methods:
function getDateKey(date) {
let year = date.getFullYear();
let month = date.getMonth();
let day = date.getDate();
return `#${year}${month}${day}`;
}
export async function switchStatusOfDate(date) {
try {
return await AsyncStorage.getItem(getDateKey(date));
} catch (e) {
// error
}
}
export async function setSwitchStatusForDate(date) {
try {
let value = `${date.getHours()}:${date.getMinutes()}`;
return await AsyncStorage.setItem(getDateKey(date), value);
} catch (e) {
// error
}
}
export async function unsetSwitchStatusForDate(date) {
try {
await AsyncStorage.removeItem(getDateKey(date));
} catch (e) {
// error
}
}
The Problem:
Currently, on load, the background color is set correctly (if the component is pressed to set the state to 'on', and an entry is written, upon reloading the application, the component background color is green). Also, when the state is loaded as 'off' (no entry exists), and I press the component, the background color changes from 'none' to green correctly. However, all further presses have no impact on the background color.
The state is being set correctly, and the AsyncStorage methods are also working. I've tried logging the switchStatus and style in the render() method, and the values returned are correct, but it doesn't have any effect on the actual render (it stays green no matter).
Does anyone know why this might be?
I feel like there's an obvious solution to this that I'm just missing, so any help would be appreciated! Thank you in advance.
Edit 1: I did alter some variable names so the code is self-contained to the question, but nothing else was changed (just in case there's any errors that are spotted caused by it).
Edit/Update 2: I've noticed that when using the element inspector with expo on my phone, the css property backgroundColor: 'none' is applied to the view, but it's background in the render is still green. This isn't due to any children components either, as I've removed them and it's still the case.
I've found that using,
backgroundColor: 'rgba(0, 0, 0, 0)'
or
backgroundColor: 'transparent'
where the last value in the first one is the opacity (in this case, completely transparent), works.
My problem was that I thought 'none' was a valid value for backgroundColor in css. This was not the case, whoops!
I am doing a random quote app and it seems like I am using react state wrong when I try to change the body background and text-box text color. The body background color is one step behind all the time and I want them to change to the same color simultaneously.
Any idea what is wrong with my code? I just added here the class component that is doing the business.
P.S. I hope my code is not too hard to read, I am a beginner
class Quote extends React.Component {
constructor() {
super();
this.state = {
quote: [],
author: [],
apiData:[],
color: ""
};
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
//Here I made an API call for this.state.apiData to get my quotes
}
handleClick(){
var randomColor = '#'+Math.floor(Math.random()*16777215).toString(16); //Here I get a random color
function getRandNum(min, max){
return Math.floor(Math.random()*(max-min)+min) //Here I get a random number in the range of my API array length
}
let randomNum = getRandNum(0,this.state.apiData.length)
this.setState({
quote: this.state.apiData[randomNum].text,
author: this.state.apiData[randomNum].author
color: randomColor
})
document.body.style.backgroundColor = this.state.color;
console.log(this.state.color)
}
render() {
return (
<div class="quote-box">
<h1 style={{color: this.state.color}}> {this.state.quote}-"{this.state.author}" </h1>
<button
onClick={this.handleClick}
class="change-button"
style={{color: "white", backgroundColor: this.state.color}}
>Change
</button>
</div>
);
}
}
Because setState sets the states asynchronously (the background color of the body is updated before the color is updated in the states). You can pass a second parameter to setState method that will be called after the states are updated.
this.setState(
{
quote: this.state.apiData[randomNum].text,
author: this.state.apiData[randomNum].author
color: randomColor
},
() => document.body.style.backgroundColor = this.state.color
)
Or you can alternatively set the background color of the body with randomColor variable.
this.setState({
quote: this.state.apiData[randomNum].text,
author: this.state.apiData[randomNum].author
color: randomColor
})
document.body.style.backgroundColor = randomColor;
Or you can use componentDidUpdate to detect the changes in states.
I change render annotations in my Mapbox every onPress call of one of the data filters buttons, as you see in this code :
import MapboxGL from "#react-native-mapbox-gl/maps";
MapboxGL.setAccessToken("mytoken_workign_fine");
export default class myClass extends Component{
render_annotaions1 = [{.....},{.....},....];
render_annotaions2 = [{.....},{.....},....];
render_annotaions3 = [{.....},{.....},....];
constructor(props) {
super(props);
this.state = {
filter_state: 1,
};
}
press_filter1(){
if(this.state.filter_state != 1){
this.setState({filter_state:1});
}
}
press_filter2(){
if(this.state.filter_state != 2){
this.setState({filter_state:2});
}
}
press_filter3(){
if(this.state.filter_state != 3){
this.setState({filter_state:3});
}
}
getAnnotations(){
switch(this.state.filter_state ){
case 1:return this.render_annotaions1;
case 2:return this.render_annotaions2;
default:return this.render_annotaions3;
}
}
render(){
return (
<MapboxGL.MapView
ref={(c) => (this._map = c)}
style={{ flex: 1 }}
rotateEnabled={false}
logoEnabled={false}
userTrackingMode={1}
pitchEnabled={false}
>
{this.getAnnotations()}
<MapboxGL.Camera
zoomLevel={1.1}
followUserLocation={false}
centerCoordinate={[9, 34]}
/>
</MapboxGL.MapView>);
}
}
now, default annotations ( annotations1 ) is showing well, but when I press on one filter button, no map changing, it keeps default annotations and this is my problem, I want to change set annotations by news assigned annotations returned by getAnnotaions() every
setState()
and
forceUpdate()
call (the both are not working ), please I need help with this problem, and thanks for all.
When using class components in React, you need to bind the methods in the constructor in order to access this. Read here: https://reactjs.org/docs/handling-events.html
constructor(props) {
super(props);
this.state = {
filter_state: 1,
};
this.press_annotation1 = this.press_annotation1.bind(this);
}
You need to do this for each click handler.
I would also recommend using a single click handler that takes in the filter state. Something like:
setFilterState(annotationNumber) {
this.setState({ filter_state: annotationNumber });
}
Lastly, always name your React components with a capital letter.
I have a stateful Key component that represents a Key in a Keyboard like so:
import React from 'react';
class Key extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
customClass: props.customClass,
value: props.value,
onAction: props.onAction,
active: false
};
this.handleAction = this.handleAction.bind(this);
this.resetActiveState = this.resetActiveState.bind(this);
}
handleAction(e) {
e.preventDefault();
this.setState ({
active: true
});
this.state.onAction(this.state.value);
//remove key pressed effect after 150 ms by resetting active state
_.delay(() => this.resetActiveState() , 150);
}
resetActiveState() {
this.setState ({
active: false
});
}
render() {
//conditionalProps is empty when active is false.
let conditionalProps = {};
let className = `default-btn ${this.state.customClass}`;
let displayValue = this.state.value;
//enable css attribute selector
if (this.state.active){
conditionalProps['data-active'] = '';
}
return (
<button id={this.state.id} key={this.state.id} className={className}
data-value={this.state.value} {...conditionalProps} onTouchStart={this.handleAction}>
{displayValue}
</button>
);
}
}
Key.defaultProps = {
customClass: '',
onAction: (val) => {}
};
export default Key;
onTouchStart is used to detect a touch event.
onTouchStart handler changes active state to true.
Component re-renders with the appropriate css to give key clicked
effect.
After 150ms, active state is set to false using resetActiveState().
Component re-renders without the key clicked effect css.
conditionalProps attribute is used to conditionally add css styles (using css attribute selector) to achieve 'key pressed' look in the rendered component.
This works as expected but I was wondering if it would be possible to refactor the component so I can extract the logic to maybe a parent component which I can then extend using the Key component.
This would be a perfect use case for a Higher Order Component.
This will allow you to abstract much of the functionality and pass it down to stateless components as props.
The React official docs do a great job of explaining how to use HOCs.