React Native state won't change - javascript

I'm working on my first game app.
What I want to do is passing GreenF to the Green component.
But the state won't change. I need GreenF to change whenever I need in the function play();
This is just the beginning of my code so nothing is full, but just for your understanding.
import React, {Component} from 'react';
import {StyleSheet, Text, View,TouchableOpacity, Button} from 'react-native';
import Green from './components/Green.js'
constructor (props){
super(props);
this.state = {
greenF: false,
score: 0,
seq: [1,2,3,1,4],
playerSeq: [],
round: 1,
}
}
play() {
this.setState({ greenF: true });
}
render(){
return (
<View>
<Button
title='start'
color='black'
onPress={this.play}>
</Button>
<Green greenFlash={this.state.greenF}> </Green>
</View>
);
}
}

You can use AsyncStorageprovided by react-native-community. It’s a persistent storage where you can set and get key-value storage.
Refer to this: https://github.com/react-native-community/async-storage
Either way you can also use redux for global state management.

First of all:
Why are you looking for a way without using state?
State would be the "React Way" of handling this information especially since I assume you want to rerender your component after a value change.
But yes you can declare variables outside your render function or even outside of your component class.
When you are starting to learn react-native right now maybe have a look at functional components and react hooks (e.g. the useState hook for state) since is it the more modern way of writing react components.
When your state grows and needs to be shared between many different components in your tree you might want to look at react contexts or even a state managment library like redux.
Edit
I am adding a functional component version of your component as well to show how you could manage your state using hooks which to me is way easier since the state update is by far less complex. Note: Functionality is still only what your example is trying to do.
import React, { useState } from 'react'
import { StyleSheet, Text, View, TouchableOpacity, Button } from 'react-native'
import Green from './components/Green.js'
const App = props => {
let score // user score
let seq = [] //order of playing colors
let playerSeq = [] //order of user pressing the colors
let round // round number
let ok //does the user pressed right
let win
let compTurn
const [ greenF, setGreenF ] = useState(false) // [value, setter] = useState(initial)
const play = () => {
seq = [ 1, 2, 3, 1, 4 ] //just for test
compTurn = true
round = 1
for (var i = 0; i < round; i++) {
switch (i) {
case 1:
setGreenF(true) // triggers rerender (after all state changes are done)
break
case 2:
//tbchanged
break
case 3:
//tbchanged
break
case 4:
//tbchanged
break
}
compTurn = false
}
}
return (
<View>
<Button title="start" color="black" onPress={play} />
<Green greenFlash={greenF}> </Green>
</View>
)
}
export default App

Related

ReactJS code structure and rendering component

I'm doing my first steps with ReactJS by trying to develop a small powerlifting application:
the user enters the weight and the plates to use are displayed on the page.
I split my code like this:
a "Plate" component:
// props :
// - color (Red, blue, yellow, green, white, silver)
// - weight(25 or 2.5, 20, 15, 10, 5, 1.25)
function Plate(props){
return <div className={`Plate ${props.color}Plate`}>
<p>{props.weight}</p>
</div>
}
export {Plate};
a "WeightInput" component:
import React, {Component} from "react"
import { Plate } from '../Plate/Plate';
import * as ReactDOM from 'react-dom/client'
class WeightInputForm extends Component{
calculation = (weight) => {
if(weight === 20) {
console.log("empty bar");
return;
}
const possibleWeights = [25, 20, 15, 10, 5, 2.5, 1.25];
const orderedPlateColor = ["Red", "Blue", "Yellow", "Green", "White", "LittleRed", "Silver"];
let plates = [];
let sideWeight = (weight-20-5)/2;
let plateNumber = 0;
possibleWeights.forEach((currentWeight, index)=>{
while(currentWeight <= sideWeight && sideWeight % currentWeight != sideWeight){
plates.push(<Plate key={plateNumber++} color={orderedPlateColor[index]} weight={currentWeight}></Plate>);
sideWeight -= currentWeight;
}
});
return plates;
}
handleSubmit = (event) =>{
event.preventDefault();
const weightSubmitted = event.target[0].value;
if(weightSubmitted === "") return;
const platesRoot = ReactDOM.createRoot(document.getElementsByClassName("Plates")[0]);
const plates = this.calculation(weightSubmitted);
platesRoot.render(plates);
}
render() {
return (<form onSubmit={this.handleSubmit}>
<input type="number" step="2.5" min={20}/>
<input type="submit" value="Submit"/>
</form>);
}
}
export default WeightInputForm;
here is the app file :
import './App.css';
import WeightInputForm from './components/WeightInput/WeightInput';
function App() {
return (
<div className="App">
<h1>How to load your powerlifting bar ?</h1>
<div className="Plates">
</div>
<div className='InputWeight'>
<WeightInputForm></WeightInputForm>
</div>
</div>
);
}
export default App;
Currently my application works as I want, however I have this warning in the console:
You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
I also tried not to use createRoot but only ReactDOM.render, obviously it is deprecated since React 17.
I have the impression that the way I qplir my application is not the right one.
Would you have any advice to remove this warning and make my code cleaner?
Thanks in advance.
You're kind of misunderstanding many things. With React the goal should be to rid of every direct contact with de DOM. document.get... should not be used.
Instead use react hooks such as useState to dynamically display HTML contain. Make use of JSX syntax. Read more on React before getting started.
In most react app render is done once and only in the index.js (root file).
There's quite a lot going on here which reflects a slightly flawed - but salvageable - understanding of how React works. Probably the key issue is how to utilise state to ensure that components properly update/reflect state changes, but I shall start at the beginning.
A minor issue is that you're mixing function components and class components. I would stick with one or the other, particularly if you're writing all the code yourself.
You should import Plate into App because that's presumably where it's going to be used. You should export it as export default Plate; just like the other components, and then import Plate from './Plate/plate';.
For this app you should be using state management. There are lots of ways to approach this but the useState hook would be the easier here. Since the output of Plate relies on the value of Input and both of those components are in App the state should be held in App.
To manage the state: create a function/handler in App that updates the state using the value from Input. Pass down that handler to the Input component, and that can call it whenever the input value changes.
As you can see in the abbreviated example (which just shows the main concepts) Plate accepts the weight state as as property. When the state changes those changes are passed down to `Plate so it can render that value.
In summary it maybe a good idea for you to visit/revisit how React actually works, and take a look at some tutorials. Understanding how React components and state work together is rather pivotal to development with the library, and it's an important step to grasp before you can build an application properly.
Here's the example.
App contains both the Plate and Input components. It has state (weight) and a way to update that state (setWeight). It also has a handler (handleChange). It passes that handler down to Input which calls it when the change event fires on the input element. When that handler is called it updates the state with the input's value.
The weight state is passed down to the Plate component. When that state changes those changes are reflected in the re-rendered component.
const { useState } = React;
// Plate accepts a weight state value
// and renders it
function Plate({ weight }) {
return <div>Plate: {weight}</div>;
}
// Input accepts the weight state value (the component
// is what is known as a controlled component), and the
// change handler. When the input element fires an event
// it calls that handler
function Input({ weight, handleChange }) {
return (
<input
type="number"
step="2.5"
min="20"
value={weight}
onChange={handleChange}
/>
);
}
function Example() {
// Initialises state with `useState`
const [ weight, setWeight ] = useState(0);
// The handler - it accepts the event from the
// element that fired it, and then grabs the value
// from the element, and then updates the state with it
function handleChange(e) {
setWeight(e.target.value);
}
// The main component contains both the Plate
// component, and the Input component, passing down
// the relevant state, and handler to each
return (
<main>
<Plate weight={weight} />
<Input
weight={weight}
handleChange={handleChange}
/>
</main>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Updating a react useState value does not work when calling useState twice

I started learning react yesterday and am having some trouble using the useState hook. The thing I am trying to do:
I have a list of challenges. Each challenge consists of an initialRound (string) and nextRounds (arr). I am trying to show the initalRound on the screen, and if there are any nextRounds, insert them as records with an initialRound in the deck. Each nextround will look like this: [index (int), roundDescription (string).
The for each loop to achieve this works. However, afterwards I want to remove the first challenge so the next challenge shows. This does not work however and it leaves me with an object undefined error on {challenges[0]['initialRound']}. If I leave the for each loop out, it does work as expected but I do not get the same nextRound formatting I try to achieve.
I hope I explained my case clear enough
import React, { useState } from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import deck from './data/challenges'
const GameScreen = () => {
const [challenges, setChallenges] = useState(deck);
const popChallengeHandler = () => {
var currentChallenge = challenges[0]
var nextRounds = currentChallenge['nextRounds']
nextRounds.forEach(round => setChallenges((prevChallenges) => prevChallenges.splice(round[0],0,{
initialRound: round[1],
nextRounds: []
})))
setChallenges((prevChallenges) => prevChallenges.splice(1))
}
return (
<View>
<Text>{challenges[0]['initialRound']}</Text>
<Button onPress={popChallengeHandler} title="Next challenge" color="red"/>
</View>
);
}
export default GameScreen; ```

Using a global object in React Context that is not related to state

I want to have a global object that is available to my app where I can retrieve the value anywhere and also set a new value anywhere. Currently I have only used Context for values that are related to state i.e something needs to render again when the value changes. For example:
import React from 'react';
const TokenContext = React.createContext({
token: null,
setToken: () => {}
});
export default TokenContext;
import React, { useState } from 'react';
import './App.css';
import Title from './Title';
import TokenContext from './TokenContext';
function App() {
const [token, setToken] = useState(null);
return(
<TokenContext.Provider value={{ token, setToken }}>
<Title />
</TokenContext.Provider>
);
}
export default App;
How would I approach this if I just want to store a JS object in context (not a state) and also change the value anywhere?
The global context concept in React world was born to resolve problem with passing down props via multiple component layer. And when working with React, we want to re-render whenever "data source" changes. One way data binding in React makes this flow easier to code, debug and maintain as well.
So what is your specific purpose of store a global object and for nothing happen when that object got changes? If nothing re-render whenever it changes, so what is the main use of it?
Prevent re-render in React has multiple ways like useEffect or old shouldComponentUpdate method. I think they can help if your main idea is just prevent re-render in some very specific cases.
Use it as state management libraries like Redux.
You have a global object (store) and you query the value through context, but you also need to add forceUpdate() because mutating the object won't trigger a render as its not part of React API:
const globalObject = { counter: 0 };
const Context = React.createContext(globalObject);
const Consumer = () => {
const [, render] = useReducer(p => !p, false);
const store = useContext(Context);
const onClick = () => {
store.counter = store.counter + 1;
render();
};
return (
<>
<button onClick={onClick}>Render</button>
<div>{globalObject.counter}</div>
</>
);
};
const App = () => {
return (
<Context.Provider value={globalObject}>
<Consumer />
</Context.Provider>
);
};

Updating component's state using props with functional components

I am trying to update the state of my component using props that I get from the parent component, but I get the following error message:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
I want the local state to update if the prop changes.
The similar posts (Updating component's state using props, Updating state with props on React child component, Updating component's state using props) did not fixed it for me.
import React, {useState} from "react"
const HomeWorld = (props) => {
const [planetData, setPlanetData] = useState([]);
if(props.Selected === true){
setPlanetData(props.Planet)
console.log(planetData)
}
return(
<h1>hi i am your starship, type: {planetData}</h1>
)
}
export default HomeWorld
You need to use the useEffect hook to run it only once.
import { useEffect } from 'react'
...
const HomeWorld = (props) => {
const [planetData, setPlanetData] = useState([]);
useEffect(() => {
if(props.Selected === true){
setPlanetData(props.Planet)
console.log(planetData)
}
}, [props.Selected, props.Planet, setPlanetData]) // This will only run when one of those variables change
return(
<h1>hi i am your starship, type: {planetData}</h1>
)
}
Please notice that if props.Selected or props.Planet change, it will re run the effect.
Why Do I Get This Error ?
Too many re-renders. React limits the number of renders to prevent an infinite loop.
What is happening here is that when your component renders, it runs everything in the function, calling setPlanetData wich will rerender the component, calling everything inside the function again (setPlanetData again) and making a infinite loop.
You're generally better off not updating your state with props. It generally makes the component hard to reason about and can often lead to unexpected states and stale data. Instead, I would consider something like:
const HomeWorld = (props) => {
const planetData = props.Selected
? props.Planet
//... what to display when its not selected, perhaps:
: props.PreviousPlanet
return(
<h1>hi i am your starship, type: {planetData}</h1>
)
}
This might require a bit more logic in the parent component, to control what displays when the Selected prop is false, but it's a lot more idiomatic React.

How can I prevent my functional component from re-rendering with React memo or React hooks?

When hiddenLogo changes value, the component is re-rendered. I want this component to never re-render, even if its props change. With a class component I could do this by implementing sCU like so:
shouldComponentUpdate() {
return false;
}
But is there a way to do with with React hooks/React memo?
Here's what my component looks like:
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import ConnectedSpringLogo from '../../containers/ConnectedSpringLogo';
import { Wrapper, InnerWrapper } from './styles';
import TitleBar from '../../components/TitleBar';
const propTypes = {
showLogo: PropTypes.func.isRequired,
hideLogo: PropTypes.func.isRequired,
hiddenLogo: PropTypes.bool.isRequired
};
const Splash = ({ showLogo, hideLogo, hiddenLogo }) => {
useEffect(() => {
if (hiddenLogo) {
console.log('Logo has been hidden');
}
else {
showLogo();
setTimeout(() => {
hideLogo();
}, 5000);
}
}, [hiddenLogo]);
return (
<Wrapper>
<TitleBar />
<InnerWrapper>
<ConnectedSpringLogo size="100" />
</InnerWrapper>
</Wrapper>
);
};
Splash.propTypes = propTypes;
export default Splash;
As G.aziz said, React.memo functions similarly to pure component. However, you can also adjust its behavior by passing it a function which defines what counts as equal. Basically, this function is shouldComponentUpdate, except you return true if you want it to not render.
const areEqual = (prevProps, nextProps) => true;
const MyComponent = React.memo(props => {
return /*whatever jsx you like */
}, areEqual);
React.memo is same thing as React.PureComponent
You can use it when you don't want to update a component that you think is static so, Same thing as PureCompoment.
For class Components:
class MyComponents extends React.PureCompoment {}
For function Components:
const Mycomponents = React.memo(props => {
return <div> No updates on this component when rendering </div>;
});
So it's just creating a component with React.memo
To verify that your component doesn't render you can just
activate HightlightUpdates in react extension and check your components reaction on
rendering
We can use memo for prevent render in function components for optimization goal only. According React document:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
According to react documentation:- [https://reactjs.org/docs/react-api.html][1]
React. memo is a higher order component. If your component renders the
same result given the same props, you can wrap it in a call to React.
memo for a performance boost in some cases by memoizing the result.
This means that React will skip rendering the component, and reuse the
last rendered result.
For practical understanding I came across these two videos they are very good if you wanna clear concepts also, better to watch so it'll save your time.
Disclaimer:- This is not my YouTube channel.
https://youtu.be/qySZIzZvZOY [ useMemo hook]
https://youtu.be/7TaBhrnPH78 [class based component]

Categories

Resources