diff --git a/exercises/src/index.css b/exercises/src/index.css index 8470480..7d4706d 100644 --- a/exercises/src/index.css +++ b/exercises/src/index.css @@ -26,3 +26,58 @@ body { flex: 3; flex-direction: column; } + +.Counter { + background-color: rgb(255, 255, 255); + list-style: none; + -webkit-padding-start: 0px; +} +.Counter__button { + display: inline-block; + -webkit-appearance: button; + user-select: none; + outline: 0px; + border-width: 0px; + border-style: initial; + border-color: initial; + border-image: initial; + border-radius: 4px; + position: relative; + -webkit-font-smoothing: antialiased; + width: 100%; + font-size: 18px; + padding: 14px 12px; + background-color: rgb(246, 104, 38); + color: white; + font-weight: 600; +} + +.Counter__button:active { + opacity: 0.8; +} + +.Counter__item { + height: 56px; + display: flex; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: justify; + justify-content: center; + position: relative; + border-bottom: 1px solid rgb(234, 234, 234); +} + +.Counter__actions { + display: flex; + -webkit-box-align: center; + align-items: center; +} + +.Counter__count { + width: 50px; + text-align: center; +} + +.Counter__disabled { + opacity: 0.3; +} \ No newline at end of file diff --git a/exercises/src/performance-optimizations/Count.jsx b/exercises/src/performance-optimizations/Count.jsx new file mode 100644 index 0000000..195dd73 --- /dev/null +++ b/exercises/src/performance-optimizations/Count.jsx @@ -0,0 +1,43 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +import subtractIcon from "./icons/subtract.svg"; +import addIcon from "./icons/add.svg"; + +export default class Count extends Component { + static propTypes = { + onIncrement: PropTypes.func, + onDecrement: PropTypes.func, + value: PropTypes.number, + } + constructor(props) { + super(props); + this.getClassNamesForButton(props); + } + getClassNamesForButton = (props) => { + const { value } = props; + this.addClassName = value >= 10 ? "Counter__disabled" : ""; + this.subtractClassName = value <= 0 ? "Counter__disabled" : ""; + } + componentWillReceiveProps(nextProps) { + this.getClassNamesForButton(nextProps); + } + render() { + const { onDecrement, value, onIncrement } = this.props; + return ( + + ); + } +} diff --git a/exercises/src/performance-optimizations/Counter.js b/exercises/src/performance-optimizations/Counter.js index 3595516..4feea5f 100755 --- a/exercises/src/performance-optimizations/Counter.js +++ b/exercises/src/performance-optimizations/Counter.js @@ -1,10 +1,8 @@ import React, { Component, Fragment } from "react"; +import Count from "./Count"; import superExpensiveFunction from "./utils/superExpensiveFunction"; class Counter extends Component { - static buttonStyle = { - padding: "5px" - }; state = { count: 0, timestamp: Date.now() @@ -14,8 +12,20 @@ class Counter extends Component { count: prevState.count + 1 }); - handleCounterClick = () => { - this.setState(this.increment); + decrement = prevState => ({ + count: prevState.count - 1 + }); + + handleIncrementClick = () => { + if (this.state.count < 10) { + this.setState(this.increment); + } + }; + + handleDecrementClick = () => { + if(this.state.count > 0) { + this.setState(this.decrement); + } }; handleDummyOperationClick = () => { @@ -24,22 +34,21 @@ class Counter extends Component { }); }; - shouldComponentUpdate(nextProps, nextState) { - // Implement shouldComponentUpdate to avoid unnecessary re-renders. - } + // Implement shouldComponentUpdate to avoid unnecessary re-renders. + // shouldComponentUpdate(nextProps, nextState) { + // } render() { const { count } = this.state; const calculatedCount = superExpensiveFunction(count); return ( -
Clicks: {calculatedCount}
-
- -
-
- -
+ +
); } diff --git a/exercises/src/performance-optimizations/Hello.js b/exercises/src/performance-optimizations/Hello.js deleted file mode 100755 index cacbb2a..0000000 --- a/exercises/src/performance-optimizations/Hello.js +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react'; - -export default ({ name }) =>

Hello {name}!

; diff --git a/exercises/src/performance-optimizations/icons/add.svg b/exercises/src/performance-optimizations/icons/add.svg new file mode 100644 index 0000000..9e5c945 --- /dev/null +++ b/exercises/src/performance-optimizations/icons/add.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/exercises/src/performance-optimizations/icons/subtract.svg b/exercises/src/performance-optimizations/icons/subtract.svg new file mode 100644 index 0000000..d9e6f93 --- /dev/null +++ b/exercises/src/performance-optimizations/icons/subtract.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/exercises/src/performance-optimizations/index.js b/exercises/src/performance-optimizations/index.js index ce315d5..c86ed1a 100755 --- a/exercises/src/performance-optimizations/index.js +++ b/exercises/src/performance-optimizations/index.js @@ -3,14 +3,15 @@ import Counter from "./Counter"; const styles = { fontFamily: "sans-serif", - textAlign: "center" + textAlign: "center", + padding: "40px 120px 0" }; -const App = () => ( +const PerformanceOptimizationExample = () => (
-

Implement shouldComponentUpdate in Counter to avoid unnecessary re-renders.

+ Implement shouldComponentUpdate in Counter & Count components to avoid unnecessary re-renders.
); -export default App +export default PerformanceOptimizationExample; diff --git a/solutions/src/performance-optimizations/Count.jsx b/solutions/src/performance-optimizations/Count.jsx new file mode 100644 index 0000000..b074620 --- /dev/null +++ b/solutions/src/performance-optimizations/Count.jsx @@ -0,0 +1,46 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +import subtractIcon from "./icons/subtract.svg"; +import addIcon from "./icons/add.svg"; + +export default class Count extends Component { + static propTypes = { + onIncrement: PropTypes.func, + onDecrement: PropTypes.func, + value: PropTypes.number, + } + constructor(props) { + super(props); + this.getClassNamesForButton(props); + } + getClassNamesForButton = (props) => { + const { value } = props; + this.addClassName = value >= 10 ? "Counter__disabled" : ""; + this.subtractClassName = value <= 0 ? "Counter__disabled" : ""; + } + componentWillReceiveProps(nextProps) { + this.getClassNamesForButton(nextProps); + } + shouldComponentUpdate(nextProps, nextState) { + return this.props.value !== nextProps.value; + } + render() { + const { onDecrement, value, onIncrement } = this.props; + return ( + + ); + } +} diff --git a/solutions/src/performance-optimizations/Counter.js b/solutions/src/performance-optimizations/Counter.js new file mode 100755 index 0000000..98ac054 --- /dev/null +++ b/solutions/src/performance-optimizations/Counter.js @@ -0,0 +1,58 @@ +import React, { Component, Fragment } from "react"; +import Count from "./Count"; +import superExpensiveFunction from "./utils/superExpensiveFunction"; + +class Counter extends Component { + state = { + count: 0, + timestamp: Date.now() + }; + + increment = prevState => ({ + count: prevState.count + 1 + }); + + decrement = prevState => ({ + count: prevState.count - 1 + }); + + handleIncrementClick = () => { + if (this.state.count < 10) { + this.setState(this.increment); + } + }; + + handleDecrementClick = () => { + if(this.state.count > 0) { + this.setState(this.decrement); + } + }; + + handleDummyOperationClick = () => { + this.setState({ + timestamp: Date.now() + }); + }; + + // Implement shouldComponentUpdate to avoid unnecessary re-renders. + shouldComponentUpdate(nextProps, nextState) { + return this.state.count !== nextState.count; + } + + render() { + const { count } = this.state; + const calculatedCount = superExpensiveFunction(count); + return ( + + + + + ); + } +} + +export default Counter; diff --git a/solutions/src/performance-optimizations/icons/add.svg b/solutions/src/performance-optimizations/icons/add.svg new file mode 100644 index 0000000..9e5c945 --- /dev/null +++ b/solutions/src/performance-optimizations/icons/add.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/solutions/src/performance-optimizations/icons/subtract.svg b/solutions/src/performance-optimizations/icons/subtract.svg new file mode 100644 index 0000000..d9e6f93 --- /dev/null +++ b/solutions/src/performance-optimizations/icons/subtract.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/solutions/src/performance-optimizations/index.js b/solutions/src/performance-optimizations/index.js new file mode 100755 index 0000000..c86ed1a --- /dev/null +++ b/solutions/src/performance-optimizations/index.js @@ -0,0 +1,17 @@ +import React from "react"; +import Counter from "./Counter"; + +const styles = { + fontFamily: "sans-serif", + textAlign: "center", + padding: "40px 120px 0" +}; + +const PerformanceOptimizationExample = () => ( +
+ Implement shouldComponentUpdate in Counter & Count components to avoid unnecessary re-renders. + +
+); + +export default PerformanceOptimizationExample; diff --git a/solutions/src/performance-optimizations/utils/superExpensiveFunction.js b/solutions/src/performance-optimizations/utils/superExpensiveFunction.js new file mode 100755 index 0000000..b6bf31e --- /dev/null +++ b/solutions/src/performance-optimizations/utils/superExpensiveFunction.js @@ -0,0 +1,6 @@ +const superExpensiveFunction = input => { + console.log("superExpensiveFunction"); + return input; +}; + +export default superExpensiveFunction;