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.
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.
Why doesn't react update the style in the setState? The color of the text does not update to green with the setState function, it remains blue.
class Practice extends Component {
state = {
count: 0,
color: "blue"
}
style = {
color: this.state.color
}
handleIncrement = () => {
this.setState({ count: this.state.count});
this.setState({ color: "green"});
}
render() {
return(
<div>
<h1 style={this.style}>
The color in this state is {this.state.color}.
The number in count is {this.state.count}.
<button
onClick={this.handleIncrement}
>
Increment
</button>
</h1>
</div>
);
}
}
Information on how a component should render should flow from state alone - this will allow you to call setState to change how the component renders. Putting a .style property on the component instance itself won't work - put it into the state instead.
Rather than duplicating the color in different parts of the state, put it in just one place, in the style object in state.
Not 100% certain, but you also probably want
this.setState({ count: this.state.count});
to be
this.setState({ count: this.state.count + 1 });
class Practice extends React.Component {
state = {
count: 0,
style: {
color: 'blue',
}
}
handleIncrement = () => {
this.setState({ count: this.state.count + 1 });
this.setState({ style: {
...this.state.style,
color: "green"
}});
}
render() {
return(
<div>
<h1 style={this.state.style}>
The color in this state is {this.state.style.color}.
The number in count is {this.state.count}.
<button
onClick={this.handleIncrement}
>
Increment
</button>
</h1>
</div>
);
}
}
ReactDOM.render(<Practice />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
While other answers explain well why your code is not working in terms of React state, there is one thing I noticed that might be another point of confusion.
When you're assigning a value to your style property like this:
style = {
color: this.state.color
}
you might be thinking that you're assigning style.color a "reference" to the string that this.state.color points to. What actually happens is that you're assigning the value "blue", because the string is a primitive type in JS. So after you've done that, your style object is really just
{
color: "blue"
}
So even if you somehow change the value of this.state.color, this fundamentally wouldn't result in updating the value of this.style.color.
state in react app will only update using setState if and only if you have inclued the state in this.state if you are using constructor or state property without constructor.
Live Demo
You should create state as:
state = {
count: 0,
style: {
color: "blue"
}
};
and update the state as:
handleIncrement = () => {
this.setState((oldState) => {
return {
count: oldState.count + 1,
style: {
...oldState.style,
color: "green"
}
};
});
};
I am new to reactJS. I want to create a component to change background images dynamically. I just write a javascript to update background image every 5 seconds but it doesn't work. These images are local images, I want to loop them and display them. Would you please take a look? Thank you very much!
import * as React from 'react';
import * as image from './resources';
const TIME_TICK_PERIOD = 5000;
var images =['resources/yellowstone.jpg','resources/yellowstone2.jpg','resources/yellowstone3.jpg'];
export class BackgroundImage extends React.Component {
constructor(props) {
super(props);
this.state = {
index:0
};
}
componentDidMount() {
this.timerID = setInterval(() => this.tick(), TIME_TICK_PERIOD);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
let idx = this.state.index;
if(idx >=images.length-1) {
this.setState({
index: 0
});
} else {
this.setState({
index: idx+1
})
}
}
render() {
return (
<div className="hints-on-home-screen">
<div
style={{
position: absolute,
backgroundImage: `url(${images[this.state.index]}`)
}}
/>
</div>
);
}
}
You are saving your initial index of 0 outside the component scope. So index + 1 is always evaluating to 1. Instead of saving the imagePath in your state, save the index in your state. Then increment it in tick. Then reference images[index].
I have a div, with player score, deaths and assists:
<div className="liveMatchPlayerScore">
{data.kill_count}/{data.death_count}/{data.assists_count}
</div>
Every time the kill or death count changes, I'd like the text to turn a bold white for 3 seconds.
I was thinking of using react-spring for this, specifically useTransitions, but the documentation shows examples using an array of items. I going to put each of the scores in an array, but it seems counterproductive.
Previously i tried wrapping the scores in an "Spring" component from react-spring but that only animated the scores on their initial render - not when they update.
How can I make the kill_count and death_count become white for 3 seconds upon changing value?
Thank you
I used #PeterAmbruzs solution, but i seem to be getting strange numbers. For example in the images below, first the score was 0/0/0 and the first number increased by 1. Instead of becoming 1/0/0, it became 01/0/0. I'm also getting absurdly high numbers for some reason. Does anyone know why this might be happening?
I have also a solution. I think it is quite simple. First you create a component for the animated numbers. I wrapped it in react.memo to update it only when its property change. You can see it is bold, and red at start, but after 3sec it became normal and black. But you can change the style whatever you want. I added skip property to prevent animation for example for the first render.
const FadeNumber = React.memo(({ value, skip }) => {
const style = useSpring({
from: { color: "red", fontWeight: "bold" },
to: { color: "black", fontWeight: "normal" },
delay: 3000
});
return (
<animated.span style={{ margin: "10px", ...(skip ? {} : style) }}>
{value}
</animated.span>
);
});
Now there is a trick to reRender the animation at property change. Simly put the value to the key. When the key changes a new component will be created, so the animation will be played again. I added a unique prefix to prevent side effects.
<FadeNumber skip={kill === 0} key={"a" + kill} value={kill} />
And the whole example:
https://codesandbox.io/s/react-spring-change-fade-out-j8ebk
https://stackblitz.com/edit/react-5sztov?file=index.js
setTimeout() is still a viable construct - even when working in React.
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
assistsCount: 0,
color: 'black',
deathCount: 0,
fontWeight: 'normal',
killCount: 0,
setStyleToReset: false,
};
}
increaseKillCount () {
this.setState((prevState, props) => {
return {
killCount: prevState.killCount + 1,
color: 'white',
fontWeight: 'bold',
setStyleToReset: true,
};
});
}
render() {
if (this.state.setStyleToReset) {
this.resetStyle();
}
return (
<div>
<div style={{
backgroundColor:'green',
}}>
<span style={{color:this.state.color, fontWeight:this.state.fontWeight}}>{this.state.killCount}</span>/{this.state.deathCount}/{this.state.assistsCount}
</div>
<button onClick={() => this.increaseKillCount()}>Increase Kill Count</button>
</div>
);
}
resetStyle() {
setTimeout(() => {
this.setState({
color: 'black',
fontWeight: 'normal',
});
}, 3000);
}
}
render(<App />, document.getElementById('root'));
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}]}