Skip to content

Commit 1992289

Browse files
committed
Update chat exercise + solution to use hooks
1 parent 5362e63 commit 1992289

File tree

2 files changed

+119
-145
lines changed

2 files changed

+119
-145
lines changed

subjects/17-Chat-App/exercise.js

Lines changed: 42 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
////////////////////////////////////////////////////////////////////////////////
1818
import "./styles.css";
1919

20-
import React from "react";
20+
import React, { useState, useEffect, useRef } from "react";
2121
import ReactDOM from "react-dom";
2222
import { login, sendMessage, subscribeToMessages } from "./utils";
2323

@@ -44,54 +44,48 @@ unsubscribe() // stop listening for new messages
4444
The world is your oyster!
4545
*/
4646

47-
class Chat extends React.Component {
48-
render() {
49-
return (
50-
<div className="chat">
51-
<header className="chat-header">
52-
<h1 className="chat-title">HipReact</h1>
53-
<p className="chat-message-count"># messages: 8</p>
54-
</header>
55-
<div className="messages">
56-
<ol className="message-groups">
57-
<li className="message-group">
58-
<div className="message-group-avatar">
59-
<img src="https://avatars1.githubusercontent.com/u/92839" />
60-
</div>
61-
<ol className="messages">
62-
<li className="message">Hey, Bruce!</li>
63-
<li className="message">
64-
So, a QA Engineer walks into a bar.
65-
</li>
66-
<li className="message">Orders a beer.</li>
67-
<li className="message">Orders 0 beers.</li>
68-
<li className="message">Orders 999999999 beers.</li>
69-
<li className="message">Orders -1 beers.</li>
70-
<li className="message">Orders a sfdeljknesv.</li>
71-
</ol>
72-
</li>
73-
<li className="message-group">
74-
<div className="message-group-avatar">
75-
<img src="https://pbs.twimg.com/profile_images/534863086276988928/bX3juDCC_400x400.jpeg" />
76-
</div>
77-
<ol className="messages">
78-
<li className="message">Ha 😅</li>
79-
</ol>
80-
</li>
81-
</ol>
82-
</div>
83-
<form className="new-message-form">
84-
<div className="new-message">
85-
<input
86-
ref="message"
87-
type="text"
88-
placeholder="say something..."
89-
/>
90-
</div>
91-
</form>
47+
function Chat() {
48+
return (
49+
<div className="chat">
50+
<header className="chat-header">
51+
<h1 className="chat-title">HipReact</h1>
52+
<p className="chat-message-count"># messages: 8</p>
53+
</header>
54+
<div className="messages">
55+
<ol className="message-groups">
56+
<li className="message-group">
57+
<div className="message-group-avatar">
58+
<img src="https://avatars1.githubusercontent.com/u/92839" />
59+
</div>
60+
<ol className="messages">
61+
<li className="message">Hey, Bruce!</li>
62+
<li className="message">
63+
So, a QA Engineer walks into a bar.
64+
</li>
65+
<li className="message">Orders a beer.</li>
66+
<li className="message">Orders 0 beers.</li>
67+
<li className="message">Orders 999999999 beers.</li>
68+
<li className="message">Orders -1 beers.</li>
69+
<li className="message">Orders a sfdeljknesv.</li>
70+
</ol>
71+
</li>
72+
<li className="message-group">
73+
<div className="message-group-avatar">
74+
<img src="https://pbs.twimg.com/profile_images/534863086276988928/bX3juDCC_400x400.jpeg" />
75+
</div>
76+
<ol className="messages">
77+
<li className="message">Ha 😅</li>
78+
</ol>
79+
</li>
80+
</ol>
9281
</div>
93-
);
94-
}
82+
<form className="new-message-form">
83+
<div className="new-message">
84+
<input type="text" placeholder="say something..." />
85+
</div>
86+
</form>
87+
</div>
88+
);
9589
}
9690

9791
ReactDOM.render(<Chat />, document.getElementById("app"));

subjects/17-Chat-App/solution.js

Lines changed: 77 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
////////////////////////////////////////////////////////////////////////////////
1818
import "./styles.css";
1919

20-
import React from "react";
20+
import React, { useState, useEffect, useRef } from "react";
2121
import ReactDOM from "react-dom";
2222
import { login, sendMessage, subscribeToMessages } from "./utils";
2323

@@ -44,59 +44,43 @@ unsubscribe() // stop listening for new messages
4444
The world is your oyster!
4545
*/
4646

47-
class SmartScroller extends React.Component {
48-
autoScroll = true;
47+
function SmartScroller(props) {
48+
const autoScroll = useRef(true);
49+
const nodeRef = useRef();
4950

50-
componentDidMount() {
51-
this.scrollToBottom();
51+
function scrollToBottom() {
52+
if (autoScroll.current) {
53+
nodeRef.current.scrollTop = nodeRef.current.scrollHeight;
54+
}
5255
}
5356

54-
componentDidUpdate() {
55-
if (this.autoScroll) this.scrollToBottom();
57+
function handleScroll() {
58+
const { scrollTop, scrollHeight, clientHeight } = nodeRef.current;
59+
const distanceToBottom = scrollHeight - (scrollTop + clientHeight);
60+
autoScroll.current = distanceToBottom < 10;
5661
}
5762

58-
scrollToBottom() {
59-
this.node.scrollTop = this.node.scrollHeight;
60-
}
63+
useEffect(scrollToBottom);
6164

62-
handleScroll = () => {
63-
const { scrollTop, scrollHeight, clientHeight } = this.node;
64-
const distanceToBottom = scrollHeight - (scrollTop + clientHeight);
65-
this.autoScroll = distanceToBottom < 10;
66-
};
67-
68-
render() {
69-
return (
70-
<div
71-
{...this.props}
72-
ref={node => (this.node = node)}
73-
onScroll={this.handleScroll}
74-
/>
75-
);
76-
}
65+
return <div {...props} ref={nodeRef} onScroll={handleScroll} />;
7766
}
7867

79-
class Chat extends React.Component {
80-
state = {
81-
user: null,
82-
messages: []
83-
};
68+
function Chat() {
69+
const [user, setUser] = useState(null);
70+
const [messages, setMessages] = useState([]);
71+
const inputRef = useRef();
8472

85-
componentDidMount() {
73+
useEffect(() => {
8674
login(user => {
87-
this.setState({ user });
88-
89-
subscribeToMessages(messages => {
90-
this.setState({ messages });
91-
});
75+
setUser(user);
76+
subscribeToMessages(setMessages);
9277
});
93-
}
78+
}, []);
9479

95-
handleSubmit = event => {
80+
function handleSubmit(event) {
9681
event.preventDefault();
9782

98-
const { user } = this.state;
99-
const messageText = this.messageInput.value;
83+
const messageText = inputRef.current.value;
10084

10185
sendMessage({
10286
userId: user.id,
@@ -106,64 +90,60 @@ class Chat extends React.Component {
10690

10791
// Clear the form.
10892
event.target.reset();
109-
};
110-
111-
render() {
112-
const { user, messages } = this.state;
113-
114-
if (user == null) return <p>Loading...</p>;
115-
116-
// Array of arrays of messages grouped by user.
117-
const messageGroups = messages.reduce((groups, message) => {
118-
const prevGroup = groups.length && groups[groups.length - 1];
119-
120-
if (prevGroup && prevGroup[0].userId === message.userId) {
121-
prevGroup.push(message);
122-
} else {
123-
groups.push([message]);
124-
}
125-
126-
return groups;
127-
}, []);
128-
129-
return (
130-
<div className="chat">
131-
<header className="chat-header">
132-
<h1 className="chat-title">HipReact</h1>
133-
<p className="chat-message-count">
134-
# messages: {messages.length}
135-
</p>
136-
</header>
137-
<SmartScroller className="messages">
138-
<ol className="message-groups">
139-
{messageGroups.map(group => (
140-
<li key={group[0].id} className="message-group">
141-
<div className="message-group-avatar">
142-
<img src={group[0].photoURL} />
143-
</div>
144-
<ol className="messages">
145-
{group.map(message => (
146-
<li key={message.id} className="message">
147-
{message.text}
148-
</li>
149-
))}
150-
</ol>
151-
</li>
152-
))}
153-
</ol>
154-
</SmartScroller>
155-
<form className="new-message-form" onSubmit={this.handleSubmit}>
156-
<div className="new-message">
157-
<input
158-
ref={node => (this.messageInput = node)}
159-
type="text"
160-
placeholder="say something..."
161-
/>
162-
</div>
163-
</form>
164-
</div>
165-
);
16693
}
94+
95+
if (user == null) return <p>Loading...</p>;
96+
97+
// Array of arrays of messages grouped by user.
98+
const messageGroups = messages.reduce((groups, message) => {
99+
const prevGroup = groups.length && groups[groups.length - 1];
100+
101+
if (prevGroup && prevGroup[0].userId === message.userId) {
102+
prevGroup.push(message);
103+
} else {
104+
groups.push([message]);
105+
}
106+
107+
return groups;
108+
}, []);
109+
110+
return (
111+
<div className="chat">
112+
<header className="chat-header">
113+
<h1 className="chat-title">HipReact</h1>
114+
<p className="chat-message-count">
115+
# messages: {messages.length}
116+
</p>
117+
</header>
118+
<SmartScroller className="messages">
119+
<ol className="message-groups">
120+
{messageGroups.map(group => (
121+
<li key={group[0].id} className="message-group">
122+
<div className="message-group-avatar">
123+
<img src={group[0].photoURL} />
124+
</div>
125+
<ol className="messages">
126+
{group.map(message => (
127+
<li key={message.id} className="message">
128+
{message.text}
129+
</li>
130+
))}
131+
</ol>
132+
</li>
133+
))}
134+
</ol>
135+
</SmartScroller>
136+
<form className="new-message-form" onSubmit={handleSubmit}>
137+
<div className="new-message">
138+
<input
139+
ref={inputRef}
140+
type="text"
141+
placeholder="say something..."
142+
/>
143+
</div>
144+
</form>
145+
</div>
146+
);
167147
}
168148

169149
ReactDOM.render(<Chat />, document.getElementById("app"));

0 commit comments

Comments
 (0)