I am rendering to the DOM with ReactDOM. I would like to know whether I have to manually unmount React components on window unload events. If so, what is the best practice here? What I can think of is the following:
class MyComponent extends React.Component {
constructure(props) {
super(props);
this.handleUnload = this.handleUnload.bind(this);
}
handleUnload() {
ReactDOM.unmountComponentAtNode(this.getDomNode());
}
componentWillMount() {
window.addEventListener('beforeunload', this.handleUnload);
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.handleUnload);
}
...
}
Please advice, and thanks.
Related
I'm learning react with mobx for state management and recently came across a scenario wherein i might have to deal with a function which could update the props of a react component.
The function is not part of the said component and that function is being exposed via the bootstrap js entry to let any external module access same and initiate re-rendering of the Component.
I'm unable to do so currently due to me being new to react js, Please suggest if it is feasible to do so in react , appreciate the help.
I've tried a few options however to no avail, i couldn't access the props of this component
PropTypes.instanceOf(SampleComponent).isRequired
Below is a sample component of mine
#observer
class SampleComponent extends React.Component {
constructor(props) {
.......
}
state = {
........
};
componentDidMount() {
..........
}
componentDidCatch(err) {
..........
}
componentWillUnmount() {
.........
}
render() {
.............
}
SampleComponent.propTypes = {
sampleProp: PropTypes.bool,
};
SampleComponent.defaultProps = {
sampleProp: false,
};
function updateSampleProp(value) {
?????
}
export default injectIntl(SampleComponent);
export { updateSampleProp }
I'm making a simple scroll-to-top component and I thought that React will only re-render a component if something in it changes. Since I have a conditional tied to state in my render, shouldn't React only render it if the state changes? Instead, I'm seeing it re-render with every little scroll.
Also, if I left it as-is, are there any downsides to it re-rendering so much?
import React from 'react';
import './scroll-to-top.css';
export default class extends React.Component {
state = {
shouldShowButton: false
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
this.setState({
shouldShowButton: window.scrollY > 250 ? true : false
});
}
render () {
{console.log("i have rendered!")}
return (
this.state.shouldShowButton ? <a className="scroll-to-top" href="#">Return to Top</a> : null
);
};
};
Welcome to Stack Overflow :)
Let's think through your code.
When the component loads, you're attaching a listener to the scroll event:
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
This fires handleScroll when the user scrolls. handleScroll sets the state of the component, regardless of whether or not the ternary condition resolves as true or false:
handleScroll = () => {
this.setState({
shouldShowButton: window.scrollY > 250 ? true : false
});
}
Whenever we use setState, React triggers render. Hence, render is triggering with every little scroll.
Downsides - you should be really careful of attaching anything to scroll, as it can affect performance. You might consider debouncing the event if you really, really need to do so. (Where debouncing is the technique of rate-limiting how many times a function can be called.)
This happens, because you are calling handleScroll function every time scroll event is fired. To fix this, setState only in condition:
import React from 'react';
import './scroll-to-top.css';
export default class extends React.Component {
state = {
shouldShowButton: false
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
const {shouldShowButton} = this.state;
if (!shouldShowButton && window.scrollY > 250) {
this.setState({
shouldShowButton: true
});
} else if (shouldShowButton && window.scrollY <= 250) {
this.setState({
shouldShowButton: false
});
}
}
render () {
{console.log("i have rendered!")}
return (
this.state.shouldShowButton ? <a className="scroll-to-top" href="#">Return to Top</a> : null
);
};
};
No, it's typical for Component. It's re-rendered(not in DOM but in virtual DOM) each time .setState is called, props are changes or parent element is re-rendered.
Just an example how re-rendering parent also fires re-rendering for child:
import React from "react";
import ReactDOM from "react-dom";
class Child extends React.Component {
render() {
console.log('child re-rendered');
return 'test';
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {a: 1};
setInterval(() => this.setState(oldState => ({...oldState, a: oldState.a + 1})), 1000);
}
render() {
return (
<div className="App">
<Child />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here you can check that child is re-rendered in the line as parent's .setState is called.
But it is not 100% to be issue for performance. Virtual DOM is much faster than browser DOM.
But if you want to avoid such a behavior you may use React.PureComponent instead of React.Component and then it will not be re-rendered on parent's update. Also PureComponent handles case when .setState does not actually changes value.
So there will be less re-rendering.
Official docs are good enough but here is also fine article at Medium
I have built a React component which is suppose to call the function on window scroll event.
I would like to call the function, "getCards('default'), when the user scroll to 90% of the scroll height.
The component looks as shown in below:
class Home extends React.Component {
constructor(props) {
super(props);
this.handleScroll = this.handleScroll.bind(this);
}
componentDidMount() {
// test
this.props.getCards('default');
window.addEventListener('scroll', this.handleScroll);
}
handleScroll(event) {
console.log('scroll event');
}
Could anyone help me achieve this?
Thank you.
You have to work with your component, please refer to this code:
class Home extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
// test
this.props.getCards('default');
}
render() {
return (<div onScroll={this.handleScroll}>...</div>)
}
handleScroll(event) {
var heightBound = window.height * 0.8
if (heightBound > window.scrollY) {
// Probably you want to load new cards?
this.props.getCards(...);
}
}
The scroll event must be placed inside your component and binded using onScroll evt. The handler will check for percentage of used screen size and will load others elements if the lower bound limit is reached.
I hope this can help :)
I've got a similar problem and nothing with onScroll work to me.
constructor(props) {
super(props);
window.addEventListener('scroll', this.handleScroll, true);
}
handleScroll = (event) => {
// Your code
}
https://gist.github.com/koistya/934a4e452b61017ad611
I want to call a promise based function before dispatching an action to the store.
The problem is that I only want to call the function when the component is going to be displayed. I use a toggle action that turns the component on and off.
Here is a sample of my code:
if ( /*component is going to be displayed*/) {
init().then(function() {
store.dispatch(toggleSomething());
});
}
else {
store.dispatch(toggleSomething());
}
Action:
export const SomethingActions = {
TOGGLE_SOMETHING: 'TOGGLE_SOMETHING'
};
export function toggleSomething() {
return {
type: SomethingActions.TOGGLE_SOMETHING
};
}
Reducer:
export default function somethingState(state = defaultState, action) {
switch (action.type) {
case somethingActions.TOGGLE_SOMETHING
return Object.assign({}, state, { open: !state.open});
default:
return state;
}
}
part of the React component:
Something.propTypes = {
display: React.PropTypes.bool.isRequired
};
function mapStateToProps(state, ownProps) {
return {
display: state.something.open
};
}
I basically want to know the value of open/display of the component above or another way to know whether the component is being displayed or not.
I don't want to pollute the render function or store a bool that changes every time I call dispatch.
Is there a way to do that?
By the sounds of it, you'd want to take advantage of React's lifecycle methods. Particularly the componentWillMount and componentWillReceiveProps.
componentWillReceiveProps does not get triggered for the initial render, so you may want to extract out the logic into a separate function so that it can be reused for both hooks:
function trigger(isDisplayed) {
if (isDisplayed) {
init().then(function() {
store.dispatch(toggleSomething());
});
}
else {
store.dispatch(toggleSomething());
}
}
componentWillMount() {
trigger(this.props.display);
}
componentWillReceiveProps(nextProps) {
trigger(nextProps.display);
}
Q1: "The problem is that I only want to call the function when the component is going to be displayed"
A1: This is definitely a problem for react lifecycle methods, in particular, componentWillMount() & componentDidMount()
Q2: "I basically want to know the value of open/display of the component above or another way to know whether the component is being displayed or not."
A2: The componentDidMount() method will be called when the component is rendered. To prevent an infinite loop where the component calls your promise on render just to call the promise again when the state changes, avoid including the toggled state in your component. Dispatch actions on component mounting that toggle the state in the store, but don't use this state in this component. This way you know whether the component is rendered without having the UI update. I hope that helps!
import React from 'react';
class StackOverFlow extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
toggleSomethingOn();
}
componentWillUnmount() {
toggleSomethingOff();
}
render() {
return (
<div>
The component has been rendered!
<br />
</div>
);
}
}
function toggleSomethingOn() {
//dispatches action to toggle state "open"
}
function toggleSomethingOff() {
//dispatches action to toggle state "closed"
}
export default StackOverFlow;
A2: If you are just looking to find out if a component has been rendered (outside of your code) you could go to your browser's developer tools and search the elements/DOM for your component html.
I have a component like:
import React, { PropTypes, Component } from 'react'
class MyView extends Component {
componentDidMount() {
window.addEventListener('onbeforeunload', this.saveState())
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.saveState())
}
saveState() {
alert("exiting")
}
render() {
return (
<div>
Something
</div>
)
}
}
export default MyView
Here when a user refresh the page I want to call a specific funtion and when call is finished I want the page to be refreshed. Same when user closes the page.
In my above code I am adding an event listener onbeforeunload and calling a saveState function.
But here my componentDidMount is working normally. onbeforeunload and saveState function is called normally when page is loaded not when page is refreshed or exited.
What is wrong in here and how can I call specific funcitn or give alert when someone exits or refresh the page in react ?
In the above code
Attach beforeunload event on top level component and on beforeunload event make render empty which will trigger componentWillUnmount of all child components.
import React, { PropTypes, Component } from 'react'
class App extends Component {
componentDidMount() {
window.addEventListener('beforeunload', () =>{
this.setState({appended:true});
});
}
render() {
if(this.state.appended){
return false;
}else{
return (<MyView />)
}
}
}
class MyView extends Component {
componentWillUnmount() {
this.saveState()
}
saveState() {
alert("exiting")
}
render() {
return (
<div>
Something
</div>
)
}
}
export default MyView
Hope this work for you.
According to this, try this syntax :
componentDidMount() {
window.addEventListener('onbeforeunload', this.saveState)
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.saveState)
}