If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThat success message is cool... but it should probably disappear after a few seconds. No problem! We can use the native setTimeout()
function to change the state back to empty after a few seconds.
Go back to RepLogApp
. Let's refactor things first: create a new method called setSuccessMessage
with a message argument. Inside, set the state.
... lines 1 - 83 | |
setSuccessMessage(message) { | |
this.setState({ | |
successMessage: message | |
}); | |
... lines 88 - 93 | |
} | |
... lines 95 - 112 |
We're making this change so that we can re-use our cool success message feature in the future. Above, instead of setting the successMessage
in the object, use this.setSuccessMessage()
and paste the text there.
... lines 1 - 39 | |
handleAddRepLog(item, reps) { | |
... lines 41 - 49 | |
createRepLog(newRep) | |
.then(repLog => { | |
... lines 52 - 60 | |
this.setSuccessMessage('Rep Log Saved!'); | |
}) | |
; | |
} | |
... lines 65 - 112 |
But! There is a downside to what we just did! Every time you change the state, React re-renders our component. Thanks to this change, it's going to re-render once for this state and then again right after. That is probably not something you need to worry about. But, as your applications grow bigger and bigger and bigger, you should be aware when you're triggering your app to re-render. This is especially important because, when a component like RepLogApp
re-renders, all of its children are also re-rendered, even if the props being passed to them don't change. And yes, there are ways to optimize this. But, for now, just be aware that re-rendering requires CPU. If you re-render a big app too often, it could slow down. But, there are ways to optimize.
Back in setSuccessMessage()
, to clear the message, use setTimeout()
, pass it an arrow function, and use this.setState()
to reset successMessage
back to empty quotes. Let's do that after 3 seconds.
... lines 1 - 84 | |
this.setState({ | |
... lines 86 - 88 | |
setTimeout(() => { | |
this.setState({ | |
successMessage: '' | |
}); | |
}, 3000) | |
} | |
... lines 95 - 112 |
Ok! Let's give this a try! Refresh and lift my big fat cat 5 times. Success! And... gone!
So easy! Except... yes... it was too easy. There's an edge case: if setSuccessMessage()
is called once, then called again 2 seconds later, the second message will disappear after only 1 second! It's a minor detail, but we can code this better.
Basically, before we call setTimeout
, we want to make sure to clear any previous timeout that may be waiting to fire. The setTimeout()
function returns an integer, which we can use to clear it. To keep track of that value, in the constructor, initialize a new property: this.successMessageTimeoutHandle = 0
.
... lines 1 - 7 | |
constructor(props) { | |
... lines 9 - 18 | |
this.successMessageTimeoutHandle = 0; | |
... lines 20 - 24 | |
} | |
... lines 26 - 119 |
This has nothing to do with React: we're just taking advantage of our object to store some data. Oh, and the value 0 is just a "null" value in disguise: if we pass this to clearTimeout()
, nothing will happen.
Back down in setSuccessMessage
, before setTimeout
, add clearTimeout(this.successMessageTimeoutHandle)
. To set that property, add this.successMessageTimeoutHandle =
before setTimeout()
.
... lines 1 - 88 | |
setSuccessMessage(message) { | |
... lines 90 - 93 | |
clearTimeout(this.successMessageTimeoutHandle); | |
this.successMessageTimeoutHandle = setTimeout(() => { | |
... lines 96 - 100 | |
} | |
... lines 102 - 119 |
And finally, to be completely on top of things, inside the callback, after we reset the state, set the timeout handle back to 0.
... lines 1 - 94 | |
this.successMessageTimeoutHandle = setTimeout(() => { | |
... lines 96 - 98 | |
this.successMessageTimeoutHandle = 0; | |
}, 3000) | |
... lines 101 - 119 |
And... we're done! Our message will always disappear a full 3 seconds after the last success message has been set. Except... yea... there is still one teenie, tiny problem... and this time, it's special to React.
Right now, RepLogApp
will always be rendered on the page. But, that's not true of React components in general. For example, we could choose to only render the RepLogCreator
component after clicking a button. Or, if you're using React Router so that users can navigate to different "pages", then even RepLogApp
would be rendered and unrendered as the user navigates.
Because of this, if your component is removed from the page, you need to ask yourself:
Is there anything I need to clean up?
The answer is usually... no! But, setTimeout()
would cause a problem. Why? Basically, if setState()
is called on a component that is not rendered to the page, React kinda freaks out. Thanks to setTimeout()
, that could happen if the component was removed right after setting a success message.
It's not a big deal, but let's clean this up. Scroll up to componentDidMount()
and add a new method: componentWillUnmount()
.
... lines 1 - 36 | |
componentWillUnmount() { | |
... line 38 | |
} | |
... lines 40 - 119 |
This is another one of those magic lifecycle functions: componentDidMount
is called right after your component is rendered to the page. componentWillUnmount
is called right before it's removed. It's your chance to clean stuff up.
Let's do that: clearTimeout(this.successMessageTimeoutHandle)
.
... lines 1 - 36 | |
componentWillUnmount() { | |
clearTimeout(this.successMessageTimeoutHandle); | |
} | |
... lines 40 - 119 |
Honestly, this isn't that common. But, keep it in mind. Another example could be if you used an external library to add some cool feature directly to a DOM element. If you want to clean that up, this is the place to do it.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": "^7.2.0",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/doctrine-bundle": "^1.6", // 1.9.1
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.3
"doctrine/doctrine-fixtures-bundle": "~3.0", // 3.0.2
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.1
"doctrine/orm": "^2.5", // v2.7.2
"friendsofsymfony/jsrouting-bundle": "^2.2", // 2.2.0
"friendsofsymfony/user-bundle": "dev-master#4125505ba6eba82ddf944378a3d636081c06da0c", // dev-master
"sensio/framework-extra-bundle": "^5.1", // v5.2.0
"symfony/asset": "^4.0", // v4.1.4
"symfony/console": "^4.0", // v4.1.4
"symfony/flex": "^1.0", // v1.17.6
"symfony/form": "^4.0", // v4.1.4
"symfony/framework-bundle": "^4.0", // v4.1.4
"symfony/lts": "^4@dev", // dev-master
"symfony/monolog-bundle": "^3.1", // v3.3.0
"symfony/polyfill-apcu": "^1.0", // v1.9.0
"symfony/serializer-pack": "^1.0", // v1.0.1
"symfony/swiftmailer-bundle": "^3.1", // v3.2.3
"symfony/twig-bundle": "^4.0", // v4.1.4
"symfony/validator": "^4.0", // v4.1.4
"symfony/yaml": "^4.0", // v4.1.4
"twig/twig": "2.10.*" // v2.10.0
},
"require-dev": {
"symfony/debug-pack": "^1.0", // v1.0.6
"symfony/dotenv": "^4.0", // v4.1.4
"symfony/maker-bundle": "^1.5", // v1.5.0
"symfony/phpunit-bridge": "^4.0", // v4.1.4
"symfony/web-server-bundle": "^4.0" // v4.1.4
}
}
// package.json
{
"dependencies": {
"@babel/plugin-proposal-object-rest-spread": "^7.12.1" // 7.12.1
},
"devDependencies": {
"@babel/preset-react": "^7.0.0", // 7.12.5
"@symfony/webpack-encore": "^0.26.0", // 0.26.0
"babel-plugin-transform-object-rest-spread": "^6.26.0", // 6.26.0
"babel-plugin-transform-react-remove-prop-types": "^0.4.13", // 0.4.13
"bootstrap": "3", // 3.3.7
"copy-webpack-plugin": "^4.4.1", // 4.5.1
"core-js": "2", // 1.2.7
"eslint": "^4.19.1", // 4.19.1
"eslint-plugin-react": "^7.8.2", // 7.8.2
"font-awesome": "4", // 4.7.0
"jquery": "^3.3.1", // 3.3.1
"promise-polyfill": "^8.0.0", // 8.0.0
"prop-types": "^15.6.1", // 15.6.1
"react": "^16.3.2", // 16.4.0
"react-dom": "^16.3.2", // 16.4.0
"sass": "^1.29.0", // 1.29.0
"sass-loader": "^7.0.0", // 7.3.1
"sweetalert2": "^7.11.0", // 7.22.0
"uuid": "^3.2.1", // 3.4.0
"webpack-notifier": "^1.5.1", // 1.6.0
"whatwg-fetch": "^2.0.4" // 2.0.4
}
}