Trying to change background by updating state in Reactjs - javascript

So I am writing an app which has several games on it.
I want the user to be able to click a button and the game appears on the screen, and the background changes color.
I want to control this using state so that the background of the whole app changes, not just the game component.
With my code the way it is now, the state changes when the game is out, but the page does not update. Any advice?
this is how my state looks
state = {
madLibsOut: false,
ticTacOut: false,
pigLatinOut: false,
background: "green",
}
here is an example of my trying to change it. The state does change, and the game appears on the screen, but the background color does not update.
handleClickTicTacToe = () => {
const out = this.state.ticTacOut
const newColor = 'red'
this.setState({ ticTacOut: true, background: newColor })
}
just in case it is useful, here is how I am setting the style:
appStyle={
backgroundColor: this.state.background,
height: "100vh",
width: "100%",
}
render() {
return (
<div className="App" style={this.appStyle}>
Thanks!

Your issue is you're setting your state as a class variable and it gets instantiated once and the value will never change.
Assuming you have ES6 enabled. You should do something similar to what #Allessandro Messori said.
But in his solution it isn't good to modify the property of an object and set the state.
When setting the state you should usually create a new object to set the state with. In this case your style object should be a new object.
state = {
madLibsOut: false,
ticTacOut: false,
pigLatinOut: false,
appStyle: {
backgroundColor: "green",
height: "100vh",
width: "100%",
}
}
handleClickTicTacToe = () => {
const out = this.state.ticTacOut
// Use Object.assign(this.state.appStyle, { backgroundColor: 'red' }); if no ES6 enabled
const newStyle = { ...this.state.appStyle, backgroundColor: 'red' };
this.setState({ ticTacOut: true, appStyle: newStyle })
}
render() {
return (
<div className="App" style={this.state.appStyle}>
Another option is to create a function that returns your style which will always update based on your state. Assuming your original code you can update your code to this.
getAppStyle = () => {
return {
backgroundColor: this.state.background,
height: "100vh",
width: "100%",
};
}
render() {
const style = this.getAppStyle();
return (
<div className="App" style={style}>

Make appStyle a function that returns the style object based on your state.
appStyle = () => {
return {
backgroundColor:this.state.backgroundColor
}
}

You should out all the appStyle object inside your component state.
Something like this should work:
state = {
madLibsOut: false,
ticTacOut: false,
pigLatinOut: false,
appStyle:{
backgroundColor: "green",
height: "100vh",
width: "100%",
}
}
handleClickTicTacToe = () => {
const out = this.state.ticTacOut
let appStyle = this.state.appStyle
appStyle.backgroundColor = 'red'
this.setState({ticTacOut: true, appStyle:appStyle})
}
render() {
return (
<div className="App" style={this.state.appStyle}>

Related

React Native: Style Dependent on State is updating, but not rendering 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!

Fabric js edit text added in canvas via input

I am using fabricjs and fabricjs-react in react app. I need the text to be edited via input. But it doesn't work. Maybe I'm missing something?
But when I enter text into the input, the canvas is not updated and the text remains the same. What am I doing wrong? How to access existing text in fabricjs?
There is my code https://codesandbox.io/s/sleepy-stitch-yvcr22
UPD: I tried to do https://codesandbox.io/s/clever-christian-zxb5ge with the addition of ID. It works. Maybe there is some more universal solution?
import React, { useEffect, useState } from 'react'
import { FabricJSCanvas, useFabricJSEditor } from 'fabricjs-react'
import { fabric } from 'fabric'
export default function App() {
const { editor, onReady } = useFabricJSEditor()
const [text, setText] = useState('')
let headText = new fabric.Text("test", {
fill: 'black',
top: 50,
left: 50,
})
const _onReady = (canvas) => {
canvas.backgroundColor = 'yellow'
canvas.setDimensions({
width: 200,
height: 200,
})
canvas.add(headText);
canvas.renderAll()
onReady(canvas)
}
useEffect(() => {
if (editor) {
headText.text = text
editor.canvas.renderAll()
}
}, [text])
return (
<>
<input onChange={(e) => setText(e.target.value)} />
<FabricJSCanvas onReady={_onReady} />
</>
)
}
The reason why it doesn't work is because whenever the setText() state is called, the App() function is called again in order to render the state changes.
Here lies the problem with this; any subsequence call to App() you end up creating a new instance of headText
let headText = new fabric.Text("test", {
fill: 'black',
top: 50,
left: 50,
})
Your useEffect is using that new instance whereas the canvas contains the very first instance. So, any changes you attempt is referring to the wrong object.
This quick dirty check proves this is a different object:
useEffect(() => {
if (editor) {
editor.canvas.getObjects()[0].text = text;
editor.canvas.renderAll()
console.log(editor.canvas.getObjects()[0] === headText);
}
}, [text])
The solution is that you need to give headText its own state so that any subsequence calls to App() the object reference is preserved.
const [headText, setTextObject] = useState(
new fabric.Text('test', {
fill: 'black',
top: 50,
left: 50,
}));

Why doesn't react update the style in the setState?

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"
}
};
});
};

State changing but not rendering

I want to render a settings menu onclick when an element is added to my list and disable all other setting menus when one is active. I also have a sample element in a different state and would like to deactivate all setting menus when it is on and have it disabled if a settings menu is on. I am storing the setting states of all elements in an array and in my update function the copy of my setting state is changing but my actual settings state isn't. I think it has to do with my useeffect and when I comment it out my settings state does change but it isn't rendered. It was working before I mapped my chart data, but I am not sure what caused it to stop working as commenting it out still does not render settings.
const [settings, setSettings] = useState([]);
const [sampSettings, setSampSettings] = useState(false);
const updateSettings = (val, index) => {
if(val){
let copySettingsFalse = [...settings]
copySettingsFalse[index]=false;
return setSettings(copySettingsFalse);
}
let copySettingsTrue = settings.fill(false);
copySettingsTrue[index] = true;
console.log(copySettingsTrue)
return setSettings(copySettingsTrue);
};
useEffect(() => {
if (stockList.length>0){
stockList.map((val,index) => {
// Chart
setLineData([
...lineData,
{
labels:trimLineDataHistory[index],
datasets: [
{
label: val.Tick,
data: trimLineDataHistory[index].reverse(),
pointRadius: 0,
fill: false,
backgroundColor: day ? "#e9dfd4" : "#141e28",
borderColor: "#52ad59",
},
],
},
]);
// Settings
setSettings([...settings, false]);
})}
return;
},[stockList]);
// Settings
// useEffect(() => {
// if (settings.includes(true)) {
// setSampSettings(false);
// }
// if (sampSettings === true) {
// setSettings(settings.fill(false));
// }
// return;
// }, [settings,sampSettings]);
if(stockList.length>1)
return(
<>
{stockList.map((val, index) => {
const { Tick, Name } = val;
return(
<div className="stockItemIcon">
<BsFillGearFill
className="gearIcon"
onClick={() => updateSettings(settings[index], index)}></BsFillGearFill>
<div className={settings[index]?
(day?"settingsContainerAlt showSettings daySettings":"settingsContainerAlt showSettings nightSettings")
:(day?"settingsContainerAlt hideSettings daySettings":"settingsContainerAlt hideSettings nightSettings")}>
</div>)}
</>)
You try to add the "Key" attribute BsFillGearFill tag and also set & update value for it. it may be any value

Make text become white upon changing value in react? React-spring?

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'));

Categories

Resources