Ternary operator issue in React's componentDidMount() - javascript

I have an issue with the ternary operator in my component. I've created a function to check if Sun's up and return boolean:
dayTime() {
const time = new Date();
const sunrise = new Date(this.state.sunrise*1000);
const sunset = new Date(this.state.sunset*1000);
return time > sunrise && time < sunset;
}
I than used ternary operator to call the function and set backgroundImage depending on boolean:
componentDidMount() {
document.body.style.backgroundImage = "url(" + (this.dayTime()? day_img_cov : night_img_cov) + ")";
}
Unfortunately, the ternary won't work properly. It picks the second image all the time. Yet when I use the ternary inside of render(), it works correctly:
render() {
return (
<div className="app" style={{backgroundImage: "url(" + (this.dayTime()? day_img : night_img) + ")"}}>
...
</div>
}

You haven't shown your full class so I don't know where this.state.sunrise and this.state.sunset is coming from, but I'm betting they're not set properly when you call this.dayTime() in componentDidMount.
Either initialize them properly in your constructor or make sure you update the background of body whenever they are modified.
The best way of doing it is your second, working, example since that's automatically run whenever the state changes - it also doesn't modify the DOM outside of the React tree, which is good practice.

Use isDaytime as a state and manipulate the classes based on that. The dayTime function can even be private outside of class (if it not uses state variables).
js
import classnames from 'classnames'
class Wallpaper extends Component {
state = {
isDaytime: daytime();
}
render() {
const { isDaytime } = this.state;
return (
<div className={classnames('bg', {
bg__daytime: isDaytime
bg__sunset: !isDaytime
})} />
)
}
}
css
.bg {
...
}
.bg__daytime {
background-image: url(day_img.png);
}
.bg__sunset {
background-image: url(night_img.png);
}

Related

React: Tell child component to "reinitialize," even when the passed props are the same

I have a MyComponent that renders a Timer component. My current setup is like this:
MyComponent.render:
render () {
return <Timer time={this.state.time} lag={this.lag || 0} />
}
Timer:
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
time: this.props.time,
};
}
startTimer = (duration) => {
if (duration > 0){
this.on = true;
let timer = duration * 1000 + this.props.lag;
var s = setInterval(() => {
this.setState({time: Math.round(timer/1000)});
timer = timer - 500;
if (timer <= 0) {
this.on = false;
clearInterval(s);
}
}, 500);
}
}
componentDidMount = () => {
this.startTimer(this.props.time);
}
render() {
return (
<div className="Timer-container">
<div className="Timer-value">{this.state.time}</div>
</div>
);
}
}
As you can see, when the Timer is initialized, it immediately starts counting down. On subsequent renders of MyComponent, I want to restart the Timer, even if the time prop doesn't change. In other words, I want it to "reinitialize" on every render. How do I achieve this?
First of all, to reset the counter, you need to store something in the state,
either the interval (so you can clear it)
or the current time (so you can set it to the initial value).
As you want to do something if the parent re-rendered (but the props didn't change), basically what you need to check is why your component updated. An answer to that would be "Trace why a React component is re-rendering"
A quick way for your example would be to check if the state has changed (not recommended):
componentDidUpdate(prevProps, prevState, snapshot) {
if( prevState === this.state ){
clearInterval( this.state.interval );
this.startTimer( this.props.time );
}
}
Another quick solution would be (if it is an option for you) to pass a shouldRerender property to the component, and then check for this property inside the component:
// -- inside MyComponent
render () {
return <Timer
time={ this.state.time }
lag={ this.lag || 0 }
shouldRerender={ {/* just an empty object */} } />;
}
// -- inside Timer
componentDidUpdate(prevProps, prevState, snapshot) {
if( prevProps.shouldRerender !== this.props.shouldRerender ){
clearInterval( this.state.interval );
this.startTimer( this.props.time );
}
}
That looks a bit "dirty" to me. A cleaner way would be to pass some state to shouldRerender, which changes on every update (e.g. just an increasing number).
However, I think the approach to check if parent rendered is not the React way. I, personally, do consider if a component renders or not an implementation detail (I don't know if that's correct to say), that is, I don't care when React decides to render, I only care for props and state (basically).
I would recommend to think about what actually is "cause and effect", what is the reason why you want to reset the timer. Probably the re-render of the parent is only the effect of some other cause, which you might be able to use for your time reset, too.
Here some different concepts that might be useful for use cases I can imagine:
not use one Time instance, but destroy and create inside parent when needed, maybe also using a key prop.
use a HOC (like withTimer) or custom hook (like useTimer), injecting a reset() function (plus create a separate TimerView component)
keep the time state in MyComponent, passing time and onChange down to the Timer component (<Timer time={ this.state.time } onChange={ time => { this.setState({ time: time }); } } />), then both MyComponent and Timer can set / reset the time.

React conditional sorting of array

I am getting a value from redux state and I wish to sort an array based conditionally on what I get from the state.
The current way I've written this is as follows:
class ProductsPage extends Component {
state = {
productArrayFromBackend: [],
productArrayToDisplay: []
}
render() {
let ProductArrayToProcess = []
if (this.props.ProductFilter == "latest") {
ProductArrayToProcess = this.state.productArrayToDisplay.sort((a, b) => (a.addedOn > b.addedOn ? -1 : 1))
} else if (this.props.ProductFilter == "popular") {
ProductArrayToProcess = this.state.productArrayToDisplay.sort((a, b) => (a.likes > b.likes ? -1 : 1))
}
else {
ProductArrayToProcess = this.state.productArrayToDisplay
}
return (
<figure>
<div className="product-warp">
{ProductArrayToProcess.map((product, i) => <ProductThumbnailElement key={i} Product={product} />)}
</div>
</figure>
);
}
}
Now, the value for this.props.ProductFilter is coming from state. What I wish to do is when the value in the redux state changes, it should change the value that is rendered inside the jsx too.
Please let me know how is that possible?
You could simply extract the your required data inside the render function.
render(){
const {ProductFilter} = this.props;
// use ProductFilter in the following code using JSX
}
Simply declaring the variable once at the begging is a better practice than calling this.props.X several time.
Furthermore, instead of calling the change items from the state directly, you might want to copy their value, and make changes upon this copy.
BTW, I'll suggest to export the ProductArrayToProcess generation to a matching function, there of course you'll still have the access of props and state.

React checkbox doesn't work as expected

I had and idea to ad some object style to React project. That is why I have written that class
export class Tagi {
constructor() {
this.members = [{
tag: "No matter",
color: "Aquamarine",
isMarked: true
}];
this.handleTagMarking = this.handleTagMarking.bind(this);
}
add(x) {
this.members.push(x);
}
static fromTable(table) {
let tags = new Tagi();
let shortened = unique(table);
for (let value of shortened) {
let record = {
tag: value,
color: colors[shortened.indexOf(value)],
isMarked: false
}
tags.add(record)
}
return tags;
}
get getAllTags() {
return this.members;
}
handleTagMarking(event){
let x = event.target.value;
const index = this.members.findIndex((element)=>element.tag==x);
const currentMarkStatus = this.members[index].isMarked;
if (currentMarkStatus) this.UnMarkTag(index); else this.MarkTag(index)
console.log(this.members)
}
The last part thereof is event handler, more about it later.
That class is implemented in Parent component like this
let Taggs =Tagi.fromTable(d);
console.log (Taggs.getMarkedTags);
Please note that this is implemented in its render function. It has nothing to do with its state.
Later in this Parent component I send it as props
render() {
const label = this.props.labels;
const SingleCheckbox =()=>{return(label.map((element)=>
<label key={element.tag} ><input type="checkbox" value={element.tag} checked={element.isMarked} onChange={this.props.fn} />{element.tag}</label>))}
return (
<div className="checkbox">
<SingleCheckbox />
</div>);
The problem is that checkbox doesn't react to checking items properly. What I mean by this is that data is changed ( I send to console the data within evenhandler so that is clear) but it does not check the fields what is expected behaviour. I suspect it happens because in Parent, Taggs are not state-linked (but only suspect) Is that correct or could there be other reason? If so, how could I easily reshape the code keeping its object oriented style?
I have just chcecked my initial idea of being-not-a-state.
That is not correct I guess. Now in constructor Taggs are initiated like this:
Taggs:Tagi.fromTable(props.disciplines.map((d) => d.tags)),
and later in render{return()} are called like this
<CheckBox labels ={this.state.Taggs.getAllTags} fn={this.state.Taggs.handleTagMarking}/>
Does not change anything at all

Function execution based on state, doesn't work correctly

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.

Proper usage of the extendability of "this" in React component

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.

Categories

Resources