perf: 优化话题更新逻辑

并增加了zustand作为插件推荐的解决方案
pull/56/head
moonrailgun 3 years ago
parent c5b79ae603
commit 5a126eb9e7

@ -1,7 +1,7 @@
const path = require('path');
module.exports = {
externalDeps: ['react', 'styled-components'],
externalDeps: ['react', 'react-router', 'axios', 'styled-components', 'zustand', 'zustand/middleware/immer'],
pluginRoot: path.resolve(__dirname, './web'),
outDir: path.resolve(__dirname, '../../public'),
};

@ -1,7 +1,7 @@
const path = require('path');
module.exports = {
externalDeps: ['react', 'styled-components'],
externalDeps: ['react', 'react-router', 'axios', 'styled-components', 'zustand', 'zustand/middleware/immer'],
pluginRoot: path.resolve(__dirname, './web'),
outDir: path.resolve(__dirname, '../../public'),
};

@ -72,7 +72,7 @@ export class AppSocket {
*/
removeListener(eventName: string, callback: (data: any) => void) {
const index = this.listener.findIndex(
(item) => item[0] === eventName && item[1] === callback
(item) => item[0] === `notify:${eventName}` && item[1] === callback
);
if (index >= 0) {
this.listener.splice(index, 1);

@ -63,7 +63,8 @@
"tailchat-shared": "*",
"tailwindcss": "^2.2.4",
"url": "^0.11.0",
"yup": "^0.32.9"
"yup": "^0.32.9",
"zustand": "^4.1.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.14.1",

@ -1,4 +1,4 @@
import { Icon } from '@/components/Icon';
import { Icon } from 'tailchat-design';
import { Dropdown } from 'antd';
import React, { useCallback, useState } from 'react';
import { useChatInputActionContext } from './context';

@ -16,6 +16,11 @@ function registerDependencies() {
regDependency('react-router', () => import('react-router'));
regDependency('axios', () => import('axios')); // 用于插件的第三方包使用axios作为依赖的情况下可以减少包体积
regDependency('styled-components', () => import('styled-components')); // 仅用于第三方插件. tailchat本身更多使用 tailwindcss
regDependency('zustand', () => import('zustand')); // 仅用于第三方插件. tailchat本身更多使用 tailwindcss
regDependency(
'zustand/middleware/immer',
() => import('zustand/middleware/immer')
); // 仅用于第三方插件. tailchat本身更多使用 tailwindcss
}
function registerModules() {

@ -32,6 +32,7 @@ class PluginManager {
await initMiniStar({
plugins,
onPluginLoadError: (err) => {
console.error('Plugin load error:', err);
loadErrorPlugins.add(err.pluginName);
},
});

@ -373,6 +373,7 @@ importers:
webpackbar: ^5.0.2
workbox-webpack-plugin: ^6.5.1
yup: ^0.32.9
zustand: ^4.1.2
dependencies:
'@loadable/component': 5.15.2_react@18.2.0
ahooks: 3.7.1_react@18.2.0
@ -415,6 +416,7 @@ importers:
tailwindcss: 2.2.19_ywsstkkounrjlah5ti55snp2aq
url: 0.11.0
yup: 0.32.11
zustand: 4.1.2_react@18.2.0
devDependencies:
'@testing-library/jest-dom': 5.16.5
'@testing-library/react': 12.1.5_biqbaboplfbrettd7655fr4n2y
@ -874,6 +876,12 @@ importers:
'@types/react-router': 5.1.18
react: 18.2.0
server/plugins/com.msgbyte.prettyinvite:
specifiers:
tailchat-server-sdk: '*'
dependencies:
tailchat-server-sdk: link:../../packages/sdk
server/plugins/com.msgbyte.simplenotify:
specifiers:
mini-star: ^1.2.8
@ -913,6 +921,7 @@ importers:
lodash: ^4.17.21
mini-star: '*'
nanoid: ^3.1.23
rollup-plugin-inject-process-env: ^1.3.1
rollup-plugin-less: ^1.1.3
tailchat-server-sdk: '*'
dependencies:
@ -923,6 +932,7 @@ importers:
'@types/react': 18.0.20
less: 4.1.3
mini-star: 1.3.1
rollup-plugin-inject-process-env: 1.3.1
rollup-plugin-less: 1.1.3
server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic:
@ -930,10 +940,12 @@ importers:
'@types/styled-components': ^5.1.26
react: 18.2.0
styled-components: ^5.3.6
zustand: ^4.1.2
devDependencies:
'@types/styled-components': 5.1.26
react: 18.2.0
styled-components: 5.3.6_react@18.2.0
zustand: 4.1.2_react@18.2.0
website:
specifiers:
@ -1345,7 +1357,7 @@ packages:
'@babel/compat-data': 7.18.13
'@babel/core': 7.18.13
'@babel/helper-validator-option': 7.18.6
browserslist: 4.21.4
browserslist: 4.21.3
semver: 6.3.0
/@babel/helper-create-class-features-plugin/7.18.13_@babel+core@7.18.13:
@ -10818,7 +10830,7 @@ packages:
babel-plugin-syntax-jsx: 6.18.0
lodash: 4.17.21
picomatch: 2.3.1
styled-components: 5.3.6_mdz3marskokvq6744hhidi3r5a
styled-components: 5.3.6_react@18.2.0
/babel-plugin-syntax-jsx/6.18.0:
resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==}
@ -18586,7 +18598,7 @@ packages:
pretty-format: 27.5.1
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1_t4lrjbt3sxauai4t5o275zsepa
ts-node: 10.9.1_bqee57coj3oib6dw4m24wknwqe
transitivePeerDependencies:
- bufferutil
- canvas
@ -25012,6 +25024,7 @@ packages:
prop-types: 15.8.1
react: 16.14.0
scheduler: 0.19.1
dev: false
/react-dom/17.0.2_react@17.0.2:
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==}
@ -25731,6 +25744,7 @@ packages:
loose-envify: 1.4.0
object-assign: 4.1.1
prop-types: 15.8.1
dev: false
/react/17.0.2:
resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==}
@ -26556,6 +26570,12 @@ packages:
transitivePeerDependencies:
- supports-color
/rollup-plugin-inject-process-env/1.3.1:
resolution: {integrity: sha512-kKDoL30IZr0wxbNVJjq+OS92RJSKRbKV6B5eNW4q3mZTFqoWDh6lHy+mPDYuuGuERFNKXkG+AKxvYqC9+DRpKQ==}
dependencies:
magic-string: 0.25.9
dev: true
/rollup-plugin-less/1.1.3:
resolution: {integrity: sha512-gvJFXpEeU5Opyz514ZO4JGj9kvFTChZEDMR3LSkSIyFfWaeE5NJMFzxPpo+MZK3CY/0j7+AotDeRofyQt9rTew==}
dependencies:
@ -26808,6 +26828,7 @@ packages:
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/scheduler/0.20.2:
resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
@ -27977,6 +27998,7 @@ packages:
react-is: 16.13.1
shallowequal: 1.1.0
supports-color: 5.5.0
dev: false
/styled-components/5.3.6_react@18.2.0:
resolution: {integrity: sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==}
@ -27998,7 +28020,6 @@ packages:
react: 18.2.0
shallowequal: 1.1.0
supports-color: 5.5.0
dev: true
/styled-system/5.1.5:
resolution: {integrity: sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==}
@ -29026,6 +29047,7 @@ packages:
typescript: 4.7.4
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: false
/ts-pnp/1.2.0_typescript@4.7.4:
resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==}
@ -29819,7 +29841,6 @@ packages:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
dev: false
/use/3.1.1:
resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
@ -31201,7 +31222,6 @@ packages:
dependencies:
react: 18.2.0
use-sync-external-store: 1.2.0_react@18.2.0
dev: false
/zwitch/1.0.5:
resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}

@ -1,7 +1,12 @@
const path = require('path');
module.exports = {
externalDeps: ['react', 'styled-components'],
externalDeps: [
'react',
'styled-components',
'zustand',
'zustand/middleware/immer',
],
pluginRoot: path.resolve(__dirname, './web'),
outDir: path.resolve(__dirname, '../../public'),
};

@ -14,6 +14,7 @@
"@types/react": "18.0.20",
"less": "^4.1.3",
"mini-star": "*",
"rollup-plugin-inject-process-env": "^1.3.1",
"rollup-plugin-less": "^1.1.3"
},
"dependencies": {

@ -10,6 +10,7 @@
"devDependencies": {
"@types/styled-components": "^5.1.26",
"react": "18.2.0",
"styled-components": "^5.3.6"
"styled-components": "^5.3.6",
"zustand": "^4.1.2"
}
}

@ -20,7 +20,11 @@ export const TopicCreate: React.FC<{
return (
<ModalWrapper title={Translate.createBtn}>
<TextArea value={text} onChange={(e) => setText(e.target.value)} />
<TextArea
autoFocus
value={text}
onChange={(e) => setText(e.target.value)}
/>
<Footer>
<Button type="primary" loading={loading} onClick={handleCreate}>

@ -1,6 +1,7 @@
import React, { useCallback, useEffect } from 'react';
import { TopicCard } from '../components/TopicCard';
import {
showSuccessToasts,
useAsyncRequest,
useGlobalSocketEvent,
useGroupPanelContext,
@ -17,6 +18,8 @@ import { request } from '../request';
import { Translate } from '../translate';
import { TopicCreate } from '../components/modals/TopicCreate';
import styled from 'styled-components';
import { useTopicStore } from '../store';
import type { GroupTopic } from '../types';
const Root = styled(LoadingOnFirst)({
display: 'flex',
@ -37,46 +40,68 @@ const Root = styled(LoadingOnFirst)({
const GroupTopicPanelRender: React.FC = React.memo(() => {
const panelInfo = useGroupPanelContext();
const { panelId, groupId } = panelInfo;
const { topicMap, addTopicPanel, addTopicItem, updateTopicItem } =
useTopicStore();
const topicList = topicMap[panelId];
const [{ value: list = [], loading }, fetch] = useAsyncRequest(async () => {
if (!panelInfo.groupId || !panelInfo.panelId) {
const [{ loading }, fetch] = useAsyncRequest(async () => {
if (!groupId || !panelId) {
return [];
}
const { data } = await request.get('list', {
params: {
groupId: panelInfo.groupId,
panelId: panelInfo.panelId,
groupId,
panelId,
},
});
return data;
}, [panelInfo.groupId, panelInfo.panelId]);
addTopicPanel(panelId, data);
}, [groupId, panelId, addTopicPanel]);
useEffect(() => {
/**
*
*/
fetch();
}, [fetch]);
useGlobalSocketEvent('plugin:com.msgbyte.topic.create', () => {
fetch(); // not good, 待优化
});
useGlobalSocketEvent(
'plugin:com.msgbyte.topic.create',
(topic: GroupTopic) => {
/**
*
*/
if (topic.panelId === panelId) {
addTopicItem(panelId, topic);
}
}
);
useGlobalSocketEvent('plugin:com.msgbyte.topic.createComment', () => {
fetch(); // not good, 待优化
});
useGlobalSocketEvent(
'plugin:com.msgbyte.topic.createComment',
(topic: GroupTopic) => {
/**
*
*/
if (topic.panelId === panelId) {
updateTopicItem(panelId, topic);
}
}
);
const handleCreateTopic = useCallback(() => {
const key = openModal(
<TopicCreate
onCreate={async (text) => {
await request.post('create', {
groupId: panelInfo.groupId,
panelId: panelInfo.panelId,
groupId,
panelId,
content: text,
});
fetch();
showSuccessToasts();
closeModal(key);
}}
/>
@ -85,8 +110,8 @@ const GroupTopicPanelRender: React.FC = React.memo(() => {
return (
<Root spinning={loading}>
{Array.isArray(list) && list.length > 0 ? (
list.map((item, i) => <TopicCard key={i} topic={item} />)
{Array.isArray(topicList) && topicList.length > 0 ? (
topicList.map((item, i) => <TopicCard key={i} topic={item} />)
) : (
<Empty description={Translate.noTopic}>
<Button type="primary" onClick={handleCreateTopic}>

@ -0,0 +1,48 @@
import create from 'zustand';
import { immer } from 'zustand/middleware/immer';
import type { GroupTopic } from './types';
interface TopicPanelMap {
[panelId: string]: GroupTopic[];
}
interface TopicStoreState {
topicMap: TopicPanelMap;
addTopicPanel: (panelId: string, topicList: GroupTopic[]) => void;
addTopicItem: (panelId: string, topic: GroupTopic) => void;
updateTopicItem: (panelId: string, topic: GroupTopic) => void;
}
export const useTopicStore = create<
TopicStoreState,
[['zustand/immer', never]]
>(
immer((set) => ({
topicMap: {},
addTopicPanel: (panelId, topicList) => {
set((state) => {
state.topicMap[panelId] = topicList;
});
},
addTopicItem: (panelId, topic) => {
set((state) => {
if (state.topicMap[panelId]) {
state.topicMap[panelId].unshift(topic);
}
});
},
updateTopicItem: (panelId, topic) => {
set((state) => {
if (state.topicMap[panelId]) {
const findedTopicIndex = state.topicMap[panelId].findIndex(
(t) => t._id === topic._id
);
if (findedTopicIndex >= 0) {
state.topicMap[panelId][findedTopicIndex] = topic;
}
}
});
},
}))
);
Loading…
Cancel
Save