Today I found something like which I was not expected. This does not happen in Angular or maybe another JS library/framework. But today I was shocked when React lifecycle methods not triggered. Is there some hack behind it. let's see the code.
I have a component A
import React, { Component } from 'react'
class A extends Component {
componentDidMount() {
console.log('component mount', this.props.name);
}
render() {
return (
<>
Hello - {this.props.name}
</>
)
}
}
export default A;
I initialized that component twice in App component with some conditions:
import React, { Component } from 'react';
import { render } from 'react-dom';
import A from './A';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
isShow: true
};
this.setIsShow = this.setIsShow.bind(this);
}
setIsShow() {
this.setState(() => {
return {isShow: !this.state.isShow};
});
}
render() {
return (
<div>
<button onClick={this.setIsShow}>Show A1</button>
<button onClick={this.setIsShow}>Show A2</button>
<br />
<br />
{this.state.isShow ? <A name="A1" />
:
<A name="A2" />
}
</div>
);
}
}
render(<App />, document.getElementById('root'));
Now, what is expected behavior? name props value changed when I clicked on buttons. that's expected, alright. But there is no lifecycle method called when component reinitialized :(. The componentDidMount fire only once.
Now add key property in A component selector and you will see componentDidMount called on every time whenever A component reinitialized.
{
this.state.isShow ? <A name="A1" key="1" />
:
<A name="A2" key="2" />
}
This is the expected behavior. But the question is why not without key property.
Checkout Online demo: https://stackblitz.com/edit/problem-react-lifecycle
What is happening here is react is optimizing the rendering for you. The same component is rendered with an updated prop vs unmounting and remounting with a different prop.
The lifecycle events are running just fine, you are just not seeing your console.log because componentDidMount is only fired on mount. If you use another lifecycle event like componentDidUpdate you will notice it fires on each change.
componentDidUpdate() {
console.log("component update", this.props.name);
}
See a live example here
When you don't specify a key react will add one internally... if the component is the same then the same key would be generated for that component meaning it wont be unmounted. There is no need to undergo the extra overhead of unmounting and remounting essentially.
However, specifying a different key for the component forces the unmount and remount because to react you've told it that these should be treated as completely different components
Edited:
This is because React is optimizing to not rerender the entire component. Adding another lifecycle method will show the expected output in the console
componentDidUpdate() {
console.log('component update', this.props.name);
}
Related
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]
I want to organize my code into components and containers folder structure. I will be using Redux with actions and reducers.
What do you think that StartButton is rather component or container? It will not be connected to the redux store, but it has its own state and some decision logic, so maybe it isn't so dumb...
I know my question has something to do with opinions, but perhaps someone can provide me with some insights and what's regarded as best practice.
Here's my StartButton component:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import './style.css';
class StartButton extends Component {
constructor() {
super();
this.state = {
startWasClicked: false,
};
}
handleStartButton = () => {
this.setState({ startWasClicked: true });
};
beerListingView() {
if (this.state.startWasClicked) {
return <div>YES! It was clicked!</div>;
}
// Else return just single <div />
return <div />;
}
render() {
return (
<div>
<div className="StartButton-container">
<RaisedButton
label="Start Here"
className="StartButton-main"
onClick={this.handleStartButton}
/>
</div>
{this.beerListingView()}
</div>
);
}
}
export default StartButton;
In my apps, containers refer to react components that are connected to the Redux store.
components are all the others. Most of them make use of the React state to toggle UI stuff for example. Thats perfectly fine.
Check out https://github.com/react-boilerplate/react-boilerplate for example
I have recently encountered an issue regarding the usage of one of my costum components. I have created a "Chargement" (Loading in French) Component for a project I am working on.
This component is a simple circular spinner with a dark background that when displayed, informs the user that an action is going on.
import React, {Fragment} from 'react';
import { CircularProgress } from 'material-ui/Progress';
import blue from 'material-ui/colors/blue';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
const styles = theme => ({
chargement: {
position: 'fixed',
left: '50%',
top: '50%',
zIndex: 1
}
});
class Chargement extends React.Component {
render () {
const { classes } = this.props;
if (this.props.chargement) {
return (
<Fragment>
<div className='loadingicon'>
<CircularProgress size={80} style={{ color: blue[500] }}/>
</div>
<div className='loadingBackground'/>
</Fragment>
);
} else {
return null;
}
}
}
const mapStateToProps = (state) => {
return {
chargement: state.App.chargement
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
}, dispatch);
};
Chargement.propTypes = {
classes: PropTypes.object.isRequired
};
let ChargementWrapped = withStyles(styles)(Chargement);
export default connect(mapStateToProps, mapDispatchToProps)(ChargementWrapped);
This component is displayed based on a boolean variable in my redux Store called "chargement".
It works like a charm whenever I am using it to make api call and load data. However, one of the components in my Web App takes quite a bit of time to render (1-2 seconds). This component renders a pretty big list of data with expansion panels. I tried to set my display variable based on the componentWillMount and componentDidMount functions.
class ListView extends React.Component {
componentWillMount () {
this.props.setChargement(true);
}
componentDidMount () {
this.props.setChargement(false);
}
However with this particular case the "chargement" component never displays.
I also tried to create a "Wrapper Component" in case the issue came from my "chargement" component being somewhat related to the re-rendered component as a children. :
export default class AppWrapper extends React.Component {
render () {
return (
<Fragment>
<Reboot />
<EnTete />
<Chargement />
<App />
</Fragment>
);
}
}
The "App " component is the one that takes a few seconds to render and that I am trying to implement my "chargement" component for. I am pretty sure this as to do with the component lifecycle but everything I tried so far failed.
My current stack is : React with Redux and MaterialUi
What am I missing ?
Thanks for your help!
Ps: You might want to check the explanation and precision I added on the main answer comments as they provide further context.
Not sure if I understood correctly, but I think the problem is simply your API call takes more time than your component mounting cycle, which is totally normal. You can solve the problem by rearranging a bit the places where to put the IO.
Assuming you are making the API call from AppWrapper, dispatch the Redux action in componentDidMount i.e. fetchListItems(). When the API call resolves, the reducer should change its internal loading value from true to false. Then, AppWrapper will receive chargement as a prop and its value will be false. Therefore, you should check what this value is in AppWrapper's render method. If the prop is true, you render the Chargement component or else, render ListView.
Also, try always to decouple the IO from the view. It's quite likely that you'll need to reuse Chargement in other situations, right? Then, make it a simple, generic component by just rendering the view. Otherwise, if you need to reuse the component, it will be coupled to one endpoint already. For this, you can use a Stateless Functional Component as follows:
const Chargement = () =>
<Fragment>
<div className='loadingicon'>
<CircularProgress size={80} style={{ color: blue[500] }}/>
</div>
<div className='loadingBackground'/>
</Fragment>
I found a way to fix my issue that does not involve the use of the "chargement" component like I had initially planned. The issue revolved around the usage of Expansion Panels from the Material-Ui-Next librairy.
The solution I found is the following :
Instead of trying to show a Loading component while my list rendered, I reduced the render time of the list by not rendering the ExpansionDetail Component unless the user clicked to expand it.
This way, the list renders well under 0.2 seconds on any devices I've tested. I set the state to collapsed: false on every panel inside the constructor.
class ListItem extends React.Component {
constructor (props) {
super(props);
this.state = {
collapsed: false
};
this.managePanelState = this.managePanelState.bind(this);
}
managePanelState () {
if (this.state.collapsed) {
this.setState({collapsed: false});
} else {
this.setState({collapsed: true});
}
}
Then I use the onChange event of the expansion panel to switch the state between collapsed and not on every ListItemDetail element.
<ExpansionPanel onChange={() => this.managePanelState()}>
I guess sometimes the solution isn't where you had initially planned.
Thanks to everyone who took time to look into my problem!
I recently have begun learning reactjs and I am having a hard time comprehending state and how it's used. I have built two stateless components (boxOne and boxTwo) and I have a property "Move Me" that I would like to pass between the two components on the click of a button (MoveButton). Below is the code to where I reached to before getting stuck
class MoveButton extends React.Component {
render() {
return (
<button className="thebutton">
Click To Move
</button>
);
}
}
class BoxOne extends React.Component {
render() {
return (
<div className="boxOne-container">
{this.props.name}
</div>
);
}
}
class BoxTwo extends React.Component {
render() {
return (
<div className="boxTwo-container">
</div>
);
}
}
function App() {
return (
<div>
<BoxOne name="Move Me" />
<BoxTwo />
<MoveButton />
</div>
);
}
ReactDOM.render(<App />,document.getElementById('container'));
Okay, so here is a codepen with everything working.
Here is the code for future generation in the event codepen dies before S-O (I think you can run it here as well??).
class Box extends React.Component {
render(){
return (
<div>
{this.props.name ? this.props.name : "nothing"}
</div>
);
}
}
class MoveButton extends React.Component {
render(){
return(
<button onClick={this.props.on_click_handler}>
Click Me
</button>
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.state = {
first_button: true
};
this.on_click_handler = this.on_click_handler.bind(this);
}
on_click_handler(){
this.setState({
first_button: !this.state["first_button"]
});
}
render() {
return (
<div>
<Box name={this.state["first_button"] ? "Move Me": null} />
<Box name={!this.state["first_button"] ? "Move Me": null} />
<MoveButton on_click_handler={this.on_click_handler} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
So, anyways... here's the explanation.
Basically what you want to do is have the higher level component deal with the state. In this case, we're talking about App. Eventually you'll start to learn where state should go, but generally you want it to be at the highest point that makes sense. Basically, in this case since the App component has the thing (the button) that is changing the state of the two Box we want the state there.
I make the actual function that deals with the click inside the App component, and pass it down to the sub component, MoveButton. I do this because the function is changing state in the App component, so it has to be there. I also had to bind the this in the constructor, which is this line: this.on_click_handler = this.on_click_handler.bind(this);. This just makes sure that this is always referencing the correct thing inside that function.
Then in that handler function I change the components state, which causes a re-render. I use the ternary operator to see which instance of Box I should be passing the "Move me" to. I also use the ternary operator in Box itself to either put the name, or "nothing" but you can change that whatever.
Hope that helps.
P.S: You don't need two different component classes for Box. They're the same thing, so just reuse the same component, but make two instances of it. Which is what I did here.
First off I'd strongly suggest to read the entire react documentation: https://facebook.github.io/react/docs/hello-world.html (or at the very least, to start off the whole quick start section, which covers all the basic you need). It covers pretty much all of react (React has quiet a small scope!).
You need to have some kind of state. Currently your class components (MoveButton, BoxOne and BoxTwo) have access to state but don't use it. Your App component defined as function does not have access to any kind of own state.
Your state needs to be in a common parent component, which you can then pass down to child components as props. The child components may be stateless. In your case that would be the App Component, which you could use a class for instead to make react state available, while the other three components you could rewrite to be stateless functions.
Now I don't understand what exactly you want to happen, I'll just assume you want to move the "Move me" text from one Box to the other on clicking the button. Therefore both boxes have the ability to display text, controlled by the parent. Both boxes could have a react prop called 'name', received by the parent (App). The button itself needs to emit an event (callback), defined in the parent and passed down to the button as prop. I'll call that prop 'handleEvent'.
The implementation could look like such:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
function BoxOne(props) {
return (
<div>BoxOne: {props.name}</div>
);
}
function BoxTwo(props) {
return (
<div>BoxTwo: {props.name}</div>
);
}
function MoveButton(props) {
return (
<button onClick={props.handleEvent}>Click to Move</button>
);
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
boxOneName: 'Move me',
boxTwoName: ''
};
this.handleEvent = this.handleEvent.bind(this);
}
handleEvent() {
this.setState({
boxOneName: this.state.boxTwoName,
boxTwoName: this.state.boxOneName
});
}
render() {
return (
<div>
<BoxOne name={this.state.boxOneName}/>
<BoxTwo name={this.state.boxTwoName}/>
<MoveButton handleEvent={this.handleEvent}/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Everything used in the example is adressed within the react quick start guide.
Let me know if anything is still unclear :)!
Can I use an instance of a reactJS component to render a component.
Eg, Let's say my reactJS component is
class myComponent extends Component{
constructor(props){
super(props)
this.state = {
next:false
}
this.alertSomething = this.alertSomething.bind(this);
this.showNext = this.showNext.bind(this);
}
showNext(){
console.log('wow');
console.log(this.state, this, this.state.next);
this.setState({next:true});
}
alertSomething(){
alert('Alert Something')
console.log(this.state, this, this.state.next);
this.setState({next:true});
}
render(){
return(
<div className='column'>
</div>
)
}
}
export default myComponent
Now, inside my another component can I do;
let x = new displayContent.renderComponent();
render(
<x />
//or
<x.render />
)
// I tried both it didn't work, I thought there mush be some other way to achieve this, after all every component is just a javascript object.
Also at the same time, can I call function to make change in its state. Like.
x.someFunction();
where someFunctino is inside that react component, doing setState.
Is it possible? OR am I missing something?
Edit: I clearly understand that when you want to render a react component, you can always do, <component />.
This question is just out of curiosity, can this be done? if not, then why?, I mean how is that different from other javascript objects.
Well, you can use the React.createElement method to render a component:
React.createElement(Component, params)
but with JSX, this is the same:
<Component />
Refer to Multiple components in the React documentation.
This is not how you're supposed to use React. You don't have to handle object instantiations ; React do this for you. Use composition instead.
render() {
return (
<myComponent />
)
}
Also, if you want to set the state of a child component from a parent component, you should probably move the logic in the parent.
Probably you are looking for something like this.
import React, { Component } from "react";
import CamCapture from './CamCapture.js';
export default class ProctorVideoFeed extends Component{
constructor(props) {
super(props);
this.Camera = React.createElement(CamCapture);
}
//this.handleVideoClick = this.handleVideoClick.bind(this);
render(){
return(
<div>
<span>{this.Camera}</span>
<button onClick = {this.Camera.StopRecording}>Stop</button>
</div>
)
}
}
Here StopRecording is a function defined inside CamCapture class.