React Cookbook
上QQ阅读APP看书,第一时间看更新

Notes – implementing componentWillReceiveProps and componentWillUnmount

In this examplewe are going to create a simple list of notes where, every 10 seconds, we will simulate that we receive an update from the service with new data, and with componentWillReceivePropswe will register the last time we got an update from the server:

  1. The componentWillReceiveProps method is called right before rendering. Like shouldComponentUpdate, it is called whenever new props are passed to the component, or the state has changed. In this example, we need to create fake data, but data normally needs to come from an actual service:
  export const notes1 = [
{
title: 'Note 1',
content: 'Content for Note 1'
},
{
title: 'Note 2',
content: 'Content for Note 2'
},
{
title: 'Note 3',
content: 'Content for Note 3'
}
];

export const notes2 = [
{
title: 'Note 4',
content: 'Content for Note 4'
},
{
title: 'Note 5',
content: 'Content for Note 5'
},
{
title: 'Note 6',
content: 'Content for Note 6'
}
];
File: src/components/Notes/data.js
  1. After we've created our fake data, let's create our component:
  import React, { Component } from 'react';
import moment from 'moment';
import './Notes.css';

const formatTime = 'YYYY-MM-DD HH:mm:ss';

class Notes extends Component {
constructor() {
super();

// We save the first date when the data is
// rendered at the beginning

this.state = {
lastUpdate: moment().format(formatTime).toString()
}
}

componentWillReceiveProps(nextProps) {
// If the prop notes has changed...
if (nextProps.notes !== this.props.notes) {
this.setState({
lastUpdate: moment().format(formatTime).toString()
});
}
}
render() {
const { notes } = this.props;

return (
<div className="Notes">
<h1>Notes:</h1>

<ul>
{notes.map((note, key) => (
<li key={key}>{note.title} - {note.content}</li>
))}
</ul>

<p>Last Update: <strong>{this.state.lastUpdate}</strong>
</p>
</div>
);
}
}

export default Notes;
File: src/components/Notes/Notes.js
  1. In this example, we are using the moment.js library. To install it, you need to run the following command:
  npm install moment
  1. Now, in our App.js file, we are going to simulate that after 10 seconds of the first render, we will receive a new update from the service and render the new notes:
  import React, { Component } from 'react';
import Notes from './Notes/Notes';
import Header from '../shared/components/layout/Header';
import Content from '../shared/components/layout/Content';
import Footer from '../shared/components/layout/Footer';

// This is our fake data...
import { notes1, notes2 } from './Notes/data';
import './App.css';

class App extends Component {
constructor() {
super();

// The first time we load the notes1...
this.state = {
notes: notes1
};
}

componentDidMount() {
// After 10 seconds (10000 milliseconds) we concatenate our
// data with notes2...

setTimeout(() => {
this.setState({
notes: [...this.state.notes, ...notes2]
});
}, 10000);
}

render() {
return (
<div className="App">
<Header title="Notes" />

<Content>
<Notes notes={this.state.notes} />
</Content>

<Footer />
</div>
);
}
}

export default App;
File: src/components/App.js
  1. The last part is the CSS file:
  .Notes {
background-color: #f5f5f5;
border-radius: 4px;
border: 1px solid #e3e3e3;
box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
margin-bottom: 20px;
margin: 50px auto;
min-height: 20px;
padding: 19px;
text-align: left;
width: 70%;
}

.Notes ul {
margin: 20px 0px;
padding: 0;
list-style: none;
}

.Notes ul li {
background-color: #fff;
border: 1px solid #ddd;
display: flex;
justify-content: space-between;
margin-bottom: -1px;
padding: 10px 15px;
position: relative;
}
File: src/components/Notes/Notes.css
  1. If you run the application, you will see something like this:
  1. After 10 seconds you will see this:
  1. As you can see, the Last Update date has changed from 2018-02-20 00:07:28 to 2018-02-20 00:07:38 (10 seconds later).
  2. componentWillUnmount: This is the last method to be called immediately before the component is removed from the DOM. Generally, is used to perform a clean-up for any DOM elements or timers created by the componentWillMount method. Let's modify our code a little bit to be able to call this method. In our Notes component, you can add this code after the render method:
  componentWillUnmount() {
console.log('Hasta la vista baby!');
document.body.style = 'background: black;';
document.getElementById('unmountMessage').style.color = 'white';
}
  1. We need to modify our index.html file to manually include a button that won't be part of React:
  <body>
<div id="root"></div>

<div id="unmountMessage">There is no mounted component!</div>

<button
id="unmount"
style="margin:0 auto;display:block;background:red;color:white;"
>
Unmount
</button>
</body>
File: public/index.html
  1. And then, in our index.js file, where we are rendering our <App /> component, let's add some extra code (we need actually to remove the element from the DOM):
  import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';

const unmountButton = document.getElementById('unmount');

// Is not very common to remove a Component from the DOM,
// but this will be just to understand how
// componentWillUnmount works.

function unmount() {
ReactDOM.unmountComponentAtNode(
document.getElementById('root')
);

document.getElementById('unmountMessage')
.style.display = 'block';

unmountButton.remove();
}

unmountButton.addEventListener('click', unmount);

document.getElementById('unmountMessage')
.style.display = 'none';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
File: src/index.js
  1. With this, we will have a hideous red button at the bottom of our page, and when we click itwe are going to unmount our component. The background will go black, and we will display the text "There is no mounted component!", and the console will display Hasta la vista baby!:

  1. After you click the button, you will see this: