From 034c440e46d3883614434ef72844b35b5d9c1ddd Mon Sep 17 00:00:00 2001
From: "Tanner Collin (aider)"
Date: Mon, 7 Jul 2025 17:40:02 +0000
Subject: [PATCH] refactor: Convert Comments class to functional using hooks
---
webclient/src/Comments.js | 155 +++++++++++++++++---------------------
1 file changed, 70 insertions(+), 85 deletions(-)
diff --git a/webclient/src/Comments.js b/webclient/src/Comments.js
index 91a517f..0da6c6b 100644
--- a/webclient/src/Comments.js
+++ b/webclient/src/Comments.js
@@ -1,35 +1,32 @@
-import React from 'react';
-import { Link } from 'react-router-dom';
+import React, { useState, useEffect, useCallback } from 'react';
+import { Link, useParams } from 'react-router-dom';
import { HashLink } from 'react-router-hash-link';
import { Helmet } from 'react-helmet';
import moment from 'moment';
import localForage from 'localforage';
import { infoLine, ToggleDot } from './utils.js';
-class Comments extends React.Component {
- constructor(props) {
- super(props);
+function countComments(c) {
+ return c.comments.reduce((sum, x) => sum + countComments(x), 1);
+}
- const id = this.props.match.params.id;
- const cache = this.props.cache;
+function Comments({ cache }) {
+ const { id } = useParams();
- if (id in cache) console.log('cache hit');
+ if (id in cache) console.log('cache hit');
- this.state = {
- story: cache[id] || false,
- error: false,
- collapsed: [],
- expanded: [],
- };
- }
-
- componentDidMount() {
- const id = this.props.match.params.id;
+ const [story, setStory] = useState(cache[id] || false);
+ const [error, setError] = useState(false);
+ const [collapsed, setCollapsed] = useState([]);
+ const [expanded, setExpanded] = useState([]);
+ useEffect(() => {
localForage.getItem(id)
.then(
(value) => {
- this.setState({ story: value });
+ if (value) {
+ setStory(value);
+ }
}
);
@@ -37,47 +34,41 @@ class Comments extends React.Component {
.then(res => res.json())
.then(
(result) => {
- this.setState({ story: result.story }, () => {
- const hash = window.location.hash.substring(1);
- if (hash) {
- document.getElementById(hash).scrollIntoView();
- }
- });
+ setStory(result.story);
localForage.setItem(id, result.story);
+ const hash = window.location.hash.substring(1);
+ if (hash) {
+ setTimeout(() => {
+ const element = document.getElementById(hash);
+ if (element) {
+ element.scrollIntoView();
+ }
+ }, 0);
+ }
},
(error) => {
- this.setState({ error: true });
+ setError(true);
}
);
- }
+ }, [id]);
- collapseComment(cid) {
- this.setState(prevState => ({
- ...prevState,
- collapsed: [...prevState.collapsed, cid],
- expanded: prevState.expanded.filter(x => x !== cid),
- }));
- }
+ const collapseComment = useCallback((cid) => {
+ setCollapsed(prev => [...prev, cid]);
+ setExpanded(prev => prev.filter(x => x !== cid));
+ }, []);
- expandComment(cid) {
- this.setState(prevState => ({
- ...prevState,
- collapsed: prevState.collapsed.filter(x => x !== cid),
- expanded: [...prevState.expanded, cid],
- }));
- }
+ const expandComment = useCallback((cid) => {
+ setCollapsed(prev => prev.filter(x => x !== cid));
+ setExpanded(prev => [...prev, cid]);
+ }, []);
- countComments(c) {
- return c.comments.reduce((sum, x) => sum + this.countComments(x), 1);
- }
-
- displayComment(story, c, level) {
+ const displayComment = useCallback((story, c, level) => {
const cid = c.author+c.date;
- const collapsed = this.state.collapsed.includes(cid);
- const expanded = this.state.expanded.includes(cid);
+ const isCollapsed = collapsed.includes(cid);
+ const isExpanded = expanded.includes(cid);
- const hidden = collapsed || (level == 4 && !expanded);
+ const hidden = isCollapsed || (level == 4 && !isExpanded);
const hasChildren = c.comments.length !== 0;
return (
@@ -88,56 +79,50 @@ class Comments extends React.Component {
{' '} | {moment.unix(c.date).fromNow()}
{hidden || hasChildren &&
- this.collapseComment(cid)}>–
+ collapseComment(cid)}>–
}
-
+
{hidden && hasChildren ?
- this.expandComment(cid)}>[show {this.countComments(c)-1} more]
+ expandComment(cid)}>[show {countComments(c)-1} more]
:
- c.comments.map(i => this.displayComment(story, i, level + 1))
+ c.comments.map(i => displayComment(story, i, level + 1))
}
);
- }
+ }, [collapsed, expanded, collapseComment, expandComment]);
- render() {
- const id = this.props.match.params.id;
- const story = this.state.story;
- const error = this.state.error;
+ return (
+
+ {error &&
Connection error?
}
+ {story ?
+
+
+ {story.title} | QotNews
+
+
- return (
-
- {error &&
Connection error?
}
- {story ?
-
-
- {story.title} | QotNews
-
-
+
{story.title}
-
{story.title}
-
-
- View article
-
-
- {infoLine(story)}
-
-
- {story.comments.map(c => this.displayComment(story, c, 0))}
-
+
+ View article
- :
-
loading...
- }
-
-
- );
- }
+
+ {infoLine(story)}
+
+
+ {story.comments.map(c => displayComment(story, c, 0))}
+
+
+ :
+
loading...
+ }
+
+
+ );
}
export default Comments;