I am currently using setState in my react-native app to render a screen.
Below is the code, for some reason the code runs everything normally except
setting the state.
showCard(event) {
const { loadCard } = this.state;
this.setState({ loadCard: true });
console.log(loadCard)
// get value of marker ID
const markerID = parseInt(event.nativeEvent.id);
console.log(markerID)
if (markerID <= this.state.markers.length && markerID != null) {
setTimeout(() => {
//USE THIS FORMAT
this.scrollView.getNode().scrollTo({
x: markerID * (CARD_WIDTH),
animated: true
});
}, 0.01);
console.log(this.scrollView)
}
}
Advice on this matter will be greatly appreciated, as it works on android for me
It does work but setting state is asynchronous so you can't just log the result after, observe with the following change:
this.setState({ loadCard: true }, () => console.log(this.state.loadCard));
The second argument is a callback fired after state is set, but you very rarely need that in reality.
Also setTimeout delay is an integer representing milliseconds not a floating point. If you want it as soon as possible use 0.
You need to bind your functions in the constructor while settling the state
constructor(props){
super(props);
this.state = {
loadCard : false;
}
this.changeCard = this.changeCard.bind(this);
}
changeCard = (userId) => {
this.setState({ loadCard: true});
}
render(){
return(
console('working state'+ this.state.loadCard);
);
}
I have somehow solved the issue. The main cause apparently as I was working with react-native maps dependency, it could not register the input directly from the marker component and required the method to be called in the main map.
But thank you for all the input guys!
Related
can someone tell me why is this "upvote" onClick handler firing twice?
the logs would indicate it's only running once but the score it controls increases by 2
export default class Container extends Component {
constructor(props) {
super(props);
this.state = {
jokes: [],
};
this.getNewJokes = this.getNewJokes.bind(this);
this.retrieveJokes = this.retrieveJokes.bind(this);
this.upVote = this.upVote.bind(this);
}
upVote(id) {
this.setState(state => {
//find the joke with the matching id and increase score by one
const modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
console.log(modifiedJokes);
return { jokes: modifiedJokes };
});
}
render() {
return (
<div>
<h1>Container</h1>
{this.state.jokes.map(joke => (
<Joke
key={joke.id}
id={joke.id}
joke={joke.joke}
score={joke.score}
upVote={this.upVote}
downVote={this.downVote}
/>
))}
</div>
);
}
}
on the other hand if I rewrite the handler this way, then it fires only once
upVote(id) {
const modifiedJokes = this.state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
this.setState({ jokes: modifiedJokes });
};
My best guess is that in the first case, you are also modifying the state directly, when you do joke.score = joke.score + 1;
Because you are doing this mapping directly on state array variable, and in Javascript, when using array, you are only working with pointer to that array, not creating a copy of that array.
So the mapping function probably takes a shallow copy of the array, and there's where problem happens.
You can use lodash to create a deep copy of the state array before you going to work with it, which will not cause your problem:
https://codesandbox.io/s/great-babbage-lorlm
This doesn't work because React uses synthetic events which are reused when it’s handling is done. Calling a function form of setState will defer evaluation (this can be dangerous when the setState call is event dependent and the event has lost its value).
So this should also work:
this.setState((state,props) => ({ jokes: modifiedJokes}));
I can't locate my source at the moment but, I remember coming across this a while back. I'm sure looking through the React Docs can provide a more in depth explanation
I found out what was wrong. I'll post an answer just in case anyone happens to stumble into this upon encountering the same problem.
When in debug mode react will run certain functions twice to discourage certain operations that might result in bugs. One of these operations is directly modifying state.
when doing
this.setState(state => {
//find the joke with the matching id and increase score by one
const modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
console.log(modifiedJokes);
return { jokes: modifiedJokes };
the map function returns a new array where every elements points to the same element in the original array. therefore by doing
state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
I was effectively directly modifying the state. React runs the setstate function twice in debug mode to weed out this kind of operation.
so the correct "react way"of modifying the attribute of an object nested in an array is to do this instead
this.setState(state =>{
let modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
return {...joke, score:joke.score+1}
}
else{ return joke}
})
return {jokes:modifiedJokes}
})
this way when encountering an element with the correct ID a copy is made of that specific element and the score of that copy is increased by one which doesn't impact the actual element which is still in the state left untouched until it is modified by react committing the new state
I have points and a function to update points:
const [points, setPoints] = useState([])
const updatePoint = (updatedPoint) => {
setPoints(points.map(point => (point.id === updatedPoint.id ? updatedPoint : point)))
}
I've added a Listener to the marker:
window.google.maps.event.addListener(marker, 'dragend',
function (markerLocal) {
console.log(getPoints)
}
)
If I click on first marker after I've created it, it shows me 1 point in the console.
If I create and click second it shows me 2 points, So it saves the state inside of the listener. However, after the second save, the sum of points for the first marker doesn't change(when it gets dragged). Is this the right behaviour? How can I get two points for the first marker? Whenever I try to update my point list and click the 1st marker, it gives me only one point - that's wrong.
Try adding useEffect with points dependency and set addEventListener for google maps. An issue here is the scope that the initial addEventListener is working on. In regular JS you would always have the current value in the scope, however, in hooks, your code will reference stale values from previous renders. In your case, you are referencing the values your points state was at the time of attaching the event to Google Maps. The cleanest solution here, in my opinion, is the Leftium's one, but you can also try this one for 'academic' purposes:
const onGoogleMapsMarkerDragend = () => {
console.log(points);
}
useEffect(() => {
const dragendListener = window.google.maps.event.addListener(marker, 'dragend', onGoogleMapsMarkerDragend);
return () => {
window.google.maps.event.removeListener(dragendListener );
}
}, [points];
Not sure if removeListener is valid - check with the documentation
This is probably because the handler uses the points at the time of declaration, which might be updated before it is invoked. My solution to such issues was to define it as a ref:
const points = useRef([]);
// This means that instead of `setPoints` you will do points.current = newValue
const updatePoint = (updatePoint) => {
points.current = points.current.map(...);
}
If a previous state is used to calculate the new state, as in your example, you should pass setPoints() a function of the old state instead of the new value directly:
const updatePoint = (updatedPoint) => {
setPoints((prevPoints) =>
prevPoints.map(point => (point.id === updatedPoint.id ? updatedPoint : point)
))
}
Relevant section from Hooks API Reference: Functional updates for useState
I want to change the temperature from Celsius to Fahrenheit (and vice-versa) but I haven't found the correct approach to tackle this, I wrote a function that does the Celsius to fahrenheit conversion but it throws me an error. So I need someone that is able to open my brain and explain this to me haha (make me understand is what I'm saying).
Here is my code: https://codepen.io/manAbl/pen/aGymRg?editors=0011
And I put in a comment the following function, that is the one that is not working:
convertingTemperature() {
const { Fahrenheit, Celcius } = this.state;
this.setState({
Fahrenheit: true,
});
const _Celcius = this.state.weather.weather && this.state.weather.main.temp;
const _Fahrenheit = Math.round(_Celcius * 5 / 9 + 32);
if(Fahrenheit) {
this.setState({ temp: _Fahrenheit })
};
}
What I want to do is hold on my state a boolean so if the fahrenheit is true, I can do the conversion and I call my function, but the reason I think is not working is because I'm pulling out the value from my weather state object, that comes from my api call. So I want to pull out that value, into a separate state so I can make the conversions with it.
--- What I want to say with this is that I want to be able to toggle between fahrenheit and celsius when clicking the temperature
I updated your code to use 2 components, where the temperature, and all that is related to the temp is defined in the WeatherData component. The temp is passed down to it using props, and is always passed down in Celcius. (NB!!!)
My CodePen
The main idea here is that you have two components, where the temp is passed down as a prop to the other component. This component also handles the conversion from C to F, and when the user clicks the span, also converts the C to F, using the function getCorrectTemp() (which also formats it as a string.
Note: remember the bind in the onClick event, or else the this context is lost.
class WeatherData extends React.Component {
constructor(props) {
super(props);
this.state = { isCelcius: true }
}
toggleIsCelcius() {
this.setState({isCelcius: !this.state.isCelcius});
}
getCorrectTemp(temp, isCelcius) {
if(this.state.isCelcius) return `${this.props.tempCelcius} C`;
return `${(this.props.tempCelcius*9/5)+32} F`;
}
render() {
const { main,name,icon,country,tempCelcius } = this.props;
if(tempCelcius) {
const tempFormatted = this.getCorrectTemp(tempCelcius, this.state.isCelcius);
return (
<div className='app-wrapper'>
<p>{name},{country}</p>
<small>{main}</small>
<span onClick={this.toggleIsCelcius.bind(this)}>{tempFormatted}</span>
<img src={icon} alt=''/>
</div>
);
} else {
return <div className='app-wrapper'>Loading ...</div>
}
}
}
I also added a loading state, but you can remove this if you do not want it. Just remember to handle the state where the temp has not yet been received from the server.
We could briefly describe that this.props is data flow from parent component and this.state is for keeping the current state of the component, and the mechanism we massively depend when we develop in React is re-rendering after setState().
If my understanding of the usage of these two are not wrong,
except holding function object, is it proper to utilize the extendability of this to hold some values considered as global variables?
For example, if I want to make 'swipe' manner available on my component, I may could do something like:
class Slider extends React.Component {
constructor(props) {
super(props);
this.state = {
movement: 0,
touchStartX: 0,
prevTouchX: 0,
beingTouched: false
};
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchMove = this.handleTouchMove.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
}
handleTouchStart(e) {
this.setState({
touchStartX: e.targetTouches[0].clientX,
beingTouched: true
});
}
handleTouchMove(e) {
if (this.state.beingTouched) {
let deltaX = e.targetTouches[0].clientX - this.state.touchStartX;
this.setState({
movement: deltaX,
prevTouchX: e.targetTouches[0].clientX
});
}
}
handleTouchEnd(e) {
// handle the sliding and set state touchStartX and beingTouched to 0 and false.
}
render() {
return (<div className = 'sliderBox'
onTouchStart = {e => this.handleTouchStart(e)}
onTouchMove = {e => this.handleTouchMove(e)}
onTouchEnd = {e => this.handleTouchEnd(e)}></div>);
}
}
export default Slider;
This is a part of my built application, it just works well. But I still wonder if it's a good way to use state property.
Or it's just OK to do something like:
class Slider extends React.Component {
constructor(props) {
super(props);
this.movement = 0;
this.touchStartX = 0;
this.prevTouchX = 0;
this.beingTouched = false;
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchMove = this.handleTouchMove.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
}
handleTouchStart(e) {
this.touchStartX = e.targetTouches[0].clientX;
this.beingTouched = true;
}
handleTouchMove(e) {
if (this.beingTouched) {
let deltaX = e.targetTouches[0].clientX - this.state.touchStartX;
this.movement = deltaX;
this.prevTouchX = e.targetTouches[0].clientX;
}
}
handleTouchEnd(e) {
// handle the sliding and set state touchStartX and beingTouched to 0 and false.
}
render() {
return (<div className = 'sliderBox'
onTouchStart = {e => this.handleTouchStart(e)}
onTouchMove = {e => this.handleTouchMove(e)}
onTouchEnd = {e => this.handleTouchEnd(e)}></div>);
}
}
export default Slider;
But it seems that the utilization of the extendability of this above is rarely seen?
Sorry if my question is meaningless, I just wonder if is there any spirit or principle to utilize the extendability of this? Props and cons?
Yes, you can attach variables directly to the component's this. It's proper in your use case.
In React's Documentation itself, in the state and lifecycle section, it gives an example of storing a timer id directly in this:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
Note how we save the timer ID right on this.
While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.
If you don’t use something in render(), it shouldn’t be in the state.
We will tear down the timer in the componentWillUnmount() lifecycle hook:
componentWillUnmount() {
clearInterval(this.timerID);
}
And as of Gleb Kost's answer, I agree that it's a normal practice.
You've nailed it. If it doesn't make sense to be in props, neither in state, feel free to attach it directly to this, if it also makes sense.
It seems that extendability of this in the react component is a normal practice, I've seen it being used in almost every more or less complex React project I have worked on.
As for deciding where to put the data in the state or on the component itself, I usually ask myself a question: does the component need to react to the change of those properties? If yes, they are going in the state, if not - on the component.
In your case, since you are using those properties only in event handlers and don't really need the component to re-render every time they change, I'd say optimal would be to utilise this, as you do.
On the other hand, if you want to use beingTouched property in the render method, for example, to change the background color of the component when it's touched than you need to put it in the state, otherwise the component would not react as expected, because it will be unaware that the property has changed.
I tried to say is I am not able to set the value using setState in sportsBasketballChange function but I am able to set it in sportsSoccerChange function
i am new to react.js
i am trying to set the value using setState.
in sportsSoccerChange function its correctly setting setState.
but sportsBasketballChange another place its not setting state properly.
can you guys tell me how to fix it.
providing my code below.
part of code
sportsSoccerChange(value) {
this.props.onChange();
let processedValue = value;
// sportsMessages sportsAirFalling
processedValue = sportsAirFallBALL(processedValue, this.props.sportsAirFall);
// sportsSuceessOnTime-ation
let sportsSuceessOnTime-ationResult = sportsSuceessOnTime-ateBALL(processedValue, this.props.sportsDrive);
if (sportsSuceessOnTime-ationResult === true) {
this.setState({ sportsOutcome: 'sportsSuceessOnTime-' });
///here i get value as sportsSuceessOnTime-
}
//this.setState({ isBALLValid: sportsSuceessOnTime-ationResult });
// formatting
processedValue = formatBALL(processedValue, this.props.sportsLongJump);
// set value in local component state
this.setState({ sportsMessagesValue: processedValue });
},
sportsBasketballChange() {
if (this.state.sportsOutcome === 'female') {
this.setState({ sportsOutcome: 'sportsSuceessOnTime-' });
///here i don't get value as sportsSuceessOnTime-
}
},
whole code here
https://gist.github.com/js08/e20c02bf21242201c1525577d55dedbc
I'm assuming that you are checking the value of this.state at those commented lines, either using logging or debugging.
setState is asynchronous. This means that there is no guarantee that the changes have occurred by the time you reach the next line of code. However, setState allows you to provide a callback function to be run after the state has finished updating. That is where you should be checking the updated value.
sportsBasketballChange() {
if (this.state.sportsOutcome === 'female') {
this.setState({ sportsOutcome: 'sportsSuceessOnTime-' },
function(){
console.log(this.state.sportsOutcome); // == 'sportsSuceessOnTime-'
}
);
console.log(this.state.sportsOutcome); // untrustworthy
}
},