I'm trying to click on a div that has the onClick function associated with it, but the function doesnt getting called due to the fact that I have a setState inside a setInterval called every 0.1 sec. This updates the DOM and doesnt let me call the function onClick.
I tried to use PureComponent and React.memo to avoid re-renders nested Components, but it didn't work; I could not use them properly though.
Inside the father constructor I have, basically, this:
setInterval(()=> {
this.setState({state1: 0})
}, 100)
}
EDIT
I'm proud of showing you the minimum (almost) code to test the issue (note the functional component: if you remove it and replace the < F /> with its content, it will work properly. Also, if you debug with google chrome, you will see what's going on the DOM):
class App extends Component {
constructor() {
super()
this.state = {state1: 0}
setInterval(() => {this.setState({state1: this.state.state1 + 1})}, 100)
}
render() {
const F = () => (
<button onClick={()=> alert("this function will be called... sometimes")}>
test: {this.state.state1}
</button>
)
return <div> <F/> </div>
}
}
EDIT 2
If I write the functional component as a pure function, it will work. here's the example:
class App extends Component {
constructor() {
super()
this.state = { state1: 0}
setInterval(() => {this.setState({state1: this.state.state1 + 1})}, 100)
}
render() {
const F = ({state1}) => (
<button onClick={()=> {alert("called sometimes")}}> test (will be called sometimes): {state1} </button>
)
function f(state1) {
return <button onClick={()=> {alert("called always")}}> test(will be called always): {state1} </button>
}
return <div> <F state1={this.state.state1}/> {f(this.state.state1)}</div>
}
}
setState will, by default rerender components that are impacted by said state.
This was answered here.
I would suggest moving away from setting state that often. That's quite expensive and I'm betting there is a far more efficient way to accomplish whatever it is that you're trying to do without the interval.
If you are using React 16.8^ then you could use Hooks to make multiple state changes. Note that instead of using setInterval i used setTimeout for each cycle
import React, {useState, useEffect} from 'react';
const App = (props) => {
const [numberShown, setNumberShown] = useState(0);
useEffect(() => {
setTimeout(() => setNumberShown(numberShown + 1), 100);
}, [numberShown]);
return (<div>
{numberShown}
</div>)
}
Hope it helps
EDIT : I found a way to do it the Component Way:
class App extends Component {
constructor() {
super()
this.state = {state1: 0}
this.startTimer = this.startTimer.bind(this)
this.startTimer()
}
startTimer = () => {
setInterval(() => {this.setState({state1: this.state.state1 + 1})}, 100)
}
render() {
return <button onClick={()=> alert("this function will be called... sometimes")}>
test: {this.state.state1}
</button>
}
}
By experimenting i noticed that if you render the button like this:
render() {
const F = () => (
<button onClick={()=> alert("this function will be called... sometimes")}>
test: {this.state.state1}
</button>
)
return <div> <F/> </div>
}
Instead of directly returning the button tag, the nested onClick function triggering the alert won't always go off but if you do it like in my example it will always trigger.
Related
I am learning reactjs and I wrote component with the method componentWillReceiveProps (cWRP) but I read that it is deprecated and it must replace with getDerivedStateFromProps (gDSFP) - https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html.
Please note that the following code has the sole purpose of illustrating my problem and questions. It is not a full code.
App.js file :
import React from 'react';
import './App.css';
import Display from './component.js'
class App extends React.Component {
state={resetCounter:false}
resetCounter= () => this.setState( {resetCounter: true} );
render() {
return (
<div className="App">
<header className="App-header">
<Display resetCounter={this.state.resetCounter}></Display>
<div>
<p></p><p></p>
<button onClick={this.resetCounter}>Reset</button>
</div>
</header>
</div>
);
}
componentDidUpdate () {
if (this.state.resetCounter!==false)
this.setState( {resetCounter: false} );
}
}
export default App;
component.js file
import React from 'react'
class Display extends React.Component {
constructor() {
super();
this.state = this.resetState();
this.state.generalCounter=0;
}
/* method to avoid code duplication in constructor and cWRP
could not be used with getDerivedStateFromProps */
resetState = () => ({resettableCounter: 0,});
componentWillReceiveProps(nextProps) {
if (nextProps.resetCounter===true)
this.setState(this.resetState())
}
render() {
return (
<>
<div>
<div>general counter : {this.state.generalCounter}</div>
<div>resettable counter : {this.state.resettableCounter}</div>
</div>
<div>
<button onClick={this.incCounters}>+</button>
<button onClick={this.decCounters}>-</button>
</div>
</>
)
}
incCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter+1,
generalCounter: this.state.generalCounter+1
}
)
decCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter-1,
generalCounter: this.state.generalCounter-1
}
)
}
export default Display
In the state of the component, there is a resettable part and a non resettable one. A method resetState is used to avoid code duplication in the constructor and in cWRP.
To replace cWRP by gDSFP, I wrote a class method because instance method could NOT be called in gDSFP (this is not usable)
...
constructor() {
super();
this.state = Display.resetState();
this.state.generalCounter=0;
}
static resetState () {
return ({resettableCounter: 0,});
}
static getDerivedStateFromProps(nextProps) {
if (nextProps.resetCounter === true) {
return Display.resetState();
} else {
return null;
}
}
...
With this solution, it is very easy to modify all my components but I am not sure that it is a good mean.
I wonder if I have a misconception and if I should rewrite my components to separate them into Fully controlled components and Fully uncontrolled components with a key ( https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions).
For example, in this case, do I have to write :
One Fully uncontrolled components for the resettable counter
One Fully controlled one for the non resettable counter
A parent component with the +/- buttons to render them.
I ask this question because in some cases, it will be much work, so I want to be sure before continuing.
You would want to keep the gdsfp version in your post if your component depends on some outside props, which you don't have controll over (such as JSON returned or 3rd party render props component, etc).
It looks like you have a full control over what's passed down to the Display. You can pass down an initial resettableCounter value down to Display.
The advantage is two-folds.
Your Display props shows what the Display does - Making it more descriptivie/readable.
It's easier to maintain, as you don't have to massage the data.
For your particular case, Fully uncontrolled component with a key seems to make more sense, as Display should accept the initial value to show, but is responsible for managing the reseetableCounter.
Unless it's absolutely unavoidable, don't create components which control their siblings (or parents). Instead, lift state to a common ancestor:
const Display = ({
generalCounter,
resettableCounter,
incrementCounters,
decrementCounters,
}) => (
<div>
<div>General Counter: {generalCounter}</div>
<div>Resettable Counter: {resettableCounter}</div>
<button onClick={incrementCounters}>Increment</button>
<button onClick={decrementCounters}>Decrement</button>
</div>
);
class DisplayContainer extends React.Component {
state = {
generalCounter: 0,
resettableCounter: 0,
};
incrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter + 1,
resettableCounter: prevState.resettableCounter + 1,
}));
decrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter - 1,
resettableCounter: prevState.resettableCounter - 1,
}));
resetResettableCounter = () => this.setState({
resettableCounter: 0,
});
render() {
return (
<React.Fragment>
<Display
{...this.state}
incrementCounters={this.incrementCounters}
decrementCounters={this.decrementCounters}
/>
<button onClick={this.resetResettableCounter}>
Reset Resettable Counter
</button>
</React.Fragment>
);
}
}
const App = () => (
<div>
<DisplayContainer />
</div>
);
An alternative approach would be something like Redux (which effectively lifts state out of React).
I'm trying to call the creator of action inside my Action.js to remove the alert after 3000ms with setTimeout(), and it outputs a number/timer 61 at the end of the message.
How can I remove this 61.
Output:
Password too short (min 6 characs.)61
Code:
const Alert = props => {
return (
<div>
{props.alert && (
<div className={`alert alert-${props.alert.type}`}>
<i className="fas fa-info-circle"> {props.alert.msg}</i>
{setTimeout(() => props.removeAlert(), 3000)}
</div>
)}
</div>
);
};
Thank you.
You should structure your code correctly like this:
const setAlert = props =>
{
if(props.alert){
setTimeout(() => props.removeAlert(), 3000)
return (props.alert.msg);
}
};
Don't put everything under the return statement if it is just action and is not generating any value for you
I strongly recommend you to read the book 'Clean Code' by Robert C. Martin
I hope this can help you:
Try to separate concerns in your component, in this case the render and the set time out:
What version of react are you using?
If you have higher that 16.8
Hooks run after each render, including the first one so: you can try something like this:
import React, { useEffect } from 'react';
useEffect(() => {
// Run your setTimeout here!!!
});
return (
// Render your component
);
Or if you are using a lower version of react you can convert your component in a class component and instead of calling the setTimeout in the render method, use it in the componentDidMount()
class Alert extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
setTimeout(() => this.props.removeAlert(), 3000)
}
render() {
return (
// Set your conditions &&
// Render your message or component
);
}
}
I'm trying to create an infinite siema carousel using algolia's instantsearch in react, but I don't think the connectors behave like React components. Should I expect componentDidMount to be called here? Suggestions? Ideas?
class ActorsClass extends connectStateResults {
constructor(props){
super(props);
var { searchState, searchResults } = props;
this.hasResults = searchResults && searchResults.nbHits !== 0;
}
componentDidMount() {
console.log("componentDidMount " + this.props.siema)
this.siema = new Siema(this.props.siema);
}
prev = () => {
this.siema.prev()
};
next = () => {
this.siema.next()
};
render = () => {
return (
<div className="actors-container">
<div xhidden={!this.hasResults}>
<h1>Actors</h1>
<InfiniteHits hitComponent={HitActors} />
</div>
<button onClick={this.prev}>Prev</button>
<button onClick={this.next}>Next</button>
</div>
);
}
Whenever the connected component receives new props they are re-invoked. It means you can use componentDidUpdate hook for your use case.
You may be interested to use reselect. See the docs for using selector.
Im trying to get this code working as a "incremental" game, where the options will be displayed after some credits, but Im getting the following warning (only once):
Warning: Can't call setState (or forceUpdate) on an unmounted component.
index.js:2178 Warning: Cannot update during an existing state
transition (such as within render or another component's
constructor). Render methods should be a pure function of props and
state; constructor side-effects are an anti-pattern, but can be moved
to componentWillMount.
Here is the code:
import React, { Component } from 'react';
import { Grid, Button } from "semantic-ui-react";
class EventDashboard extends Component {
constructor(props) {
super(props)
this.state={
credits: 0,
newOption: false,
newOptionDirty: false,
}
this.addCredits = this.addCredits.bind(this)
this.renderNewOption = this.renderNewOption.bind(this)
this.intervalID = null;
}
addCredits() {
this.setState((previousState) => ({
credits: previousState.credits + 1
}))
}
componentDidMount() {
this.intervalID = setInterval(() => {
this.addCredits()
}, 1000)
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
renderNewOption() {
if(this.state.credits === 5 && !this.state.newOptionDirty) {
// the warning happens here
this.setState(() =>({
newOptionDirty: true
}))
}
if(this.state.newOptionDirty) {
return(
<div>
<Button> Add new feature </Button>
</div>
)
} else {
return (
<div>no options until 5 credits</div>
)
}
}
render() {
return (
<Grid>
<Grid.Column width={10}>
<Button
onClick={this.addCredits}
>AddCredits</Button>
{this.renderNewOption()}
</Grid.Column>
<Grid.Column width={6}>
<h1>Credits</h1>
<h2>{this.state.credits}</h2>
</Grid.Column>
</Grid>
)
}
}
export default EventDashboard
In spite of this warning everything is working fine.
What good practice Im missing?
The problem is indeed with your renderNewOption() method and how you use it.
You are invoking renderNewOption() from render(), which is bad because you are doing a setState() in it. Remember that whenever you do setState() you update the state and trigger a new render. As such, having setState() inside the render() would create an infinite loop since the render function would keep calling itself.
In this particular case, you won't actually get an infinite loop because you have an if-case that prevents that, however your component will run it the first time (when newOptionDirty is still false). When that happens, your component hasn't mounted yet because the render() never finished before the this particular setState() was invoked.
TL;DR: Never call setState() during a render.
Try this instead:
componentDidMount() {
setInterval(() => {
this.addCredits()
}, 1000);
}
addCredits() {
this.setState((previousState) => ({
credits: previousState.credits + 1
});
}
renderNewOption() {
if(this.state.credits >= 5) {
return(
<div>
<Button> Add new feature </Button>
</div>
)
} else {
return (
<div>no options until 5 credits</div>
)
}
}
We don't need (and shouldn't) to create some new state variable inside the render. All we are interested in is if credits are equal to 5 or not.
Depending on its value, we return the appropriate React Element (button or no button).
Whenever you are doing any asynchronous stuff in your component like callbacks/Promises/setTimeout you might run into a situation when a component might have already been unmounted and you will try to update umounted component ( like in your case ) which might lead to memory leaks. This is one of the use cases for external state management libraries and middlewares like redux and redux-saga/redux-observable.
If you are going to do it in your components however you need to take care to do necessary cleanups when component unmounts. This is what for componentWillUnmount is used for:
constructor(props) {
super(props)
...
this.intervalID = null;
}
componentDidMount() {
this.intervalID = setInterval(() => {
this.addCredits()
}, 1000)
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
I'm trying to get MobX to work with functional components in react. I want to do this without having to use decorators. I have set up an app with create-react-app, added MobX and MobX-react as dependencies.
However, I can't seem to get observables working within functional components.
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
const Test = () => {
extendObservable(this, {
button: false
});
const handleB1 = () => {
this.button = false;
}
const handleB2 = () => {
this.button = true;
}
const getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={handleB2}>Button 2</button>
);
};
const getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={handleB1}>Button 1</button>
);
};
return (
<div>
{this.button ? getButton1() : getButton2()}
</div>
)
};
export default observer(Test);
Clicking the button I would expect the component to get rerendered due to the observable being changed, but I get an error:
×
Error: [mobx] Invariant failed: Side effects like changing state are not
allowed at this point. Are you trying to modify state from, for example, the render
function of a React component? Tried to modify: ObservableObject#2.button
I have tried declaring the observable as part of a functional component or before like this:
const buttonState = () => {
extendObservable(this, {
button: false
});
}
but in both cases I could not get the component to rerender or i was not sure if the observable was actually correctly set.
If i write the whole thing as a class like this it works perfectly
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
class Test extends React.Component {
constructor(props) {
super();
extendObservable(this, {
button: false
});
}
handleB1 = () => {
this.button = false;
}
handleB2 = () => {
this.button = true;
}
getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={this.handleB2}>Button 2</button>
);
};
getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={this.handleB1}>Button 1</button>
);
};
render = () => {
return (
<div>
{this.button ? this.getButton1() : this.getButton2()}
</div>
)
}
};
export default observer(Test);
In React, functional components are not persistent. They run from top to bottom, return some JSX, rinse and repeat.
let i = 0;
const FunctionalComp = (props) => {
const foo = props.foo.toUpperCase();
return <span>Rendered {i++} times. {foo}</span>;
}
All this functional component will ever do is synchronously create the value foo and then return the span. When this component's parent re-renders, this component will do the same exact same, but with potentially new values.
It can never do anything else, and that is why it is powerful. That is why we can call it a functional component: Because it only depends on the values provided to it, because it does not cause side effects that would alter the direction of the rest of the application, and because given the same arguments, this function will produce the same result for the rest of eternity.
Predictable = powerful.
Now, a class component holds persistent state. It constructs, initializes its state, methods, and properties, and then renders and returns JSX. The class (object) still exists in memory, so all of the values and methods on it exist too.
The methods of class component are not so predictable.
class Foo {
name = 'tommy';
getUpperName() {
return this.name.toUpperCase();
}
setName(name) {
this.name = name;
}
}
Foo.getUpperName will not produce the same result every time it is ever used with the same arguments (hint: it doesn't accept any arguments and depends on the context around it to determine its result), so other pieces of the application may change Foo.name and, essentially, control Foo.getUpperName's outcome, potentially by accident.
The class can update its own state, causing itself and all children components to re-compute their JSX returns.
In a plain arrow function, after it returns, all that exists is the return value that it produces and the function statement (declaration). Nothing in between.
All this said, the functional component has no this bound to it. (That is a no-no in functional programming.) It will never have state.
So you can not do anything with this inside of a functional component and it can not hold observable values because every time it re-renders it would re-instantiate each of those values.
In your example above, even if this did refer to Test, this is what would happen:
Test would create the observable value button as false.
Test would render the button, which you would then click.
Test would create the observable value button as false.
Test would render the button, which you would then click.
Test would create the observable value button as false.
Test would render the button, which you would then click.
So on and so forth.
In MobX, your observables need to live on a persistent data structure and be passed into your render functions that return UI markup.
const store = observable({
name: 'tommy'
});
const changeName = () => store.name = store.name.split('').reverse().join('');
const Foo = observer((props) => {
return (
<button onClick={changeName}>{store.name}'s button</button>
)
});
This is not a great pattern, as neither Foo nor changeName are pure, this code would work, at least.
You need to do something like so:
const store = () => {
const self = {};
self.actions = {
setName: action((name) => self.name = name);
}
return extendObservable(self, { name: 'tommy' });
}
const App = (props) => {
return <span><Foo store={store} /></span>
}
const Foo = observer((props) => {
return (
<button onClick={props.store.actions.setName}>
{store.name}'s button
</button>
)
})
Again, this is not an ideal implementation, but it would work, and I am at work and have to get back to what they pay me to do. ;)