From 1b18bd39aadc48bfc40e6e40d9020def1a61bcd2 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Sat, 14 Nov 2020 18:02:34 +0200 Subject: [PATCH] Use memoization to avoid unnecessary renders --- .../TopicList/TopicListRow/index.jsx | 58 +++++++++------ .../src/components/TopicList/index.jsx | 71 ++++++++----------- .../src/layouts/MainLayout/index.jsx | 1 - .../src/views/Home/Board/index.jsx | 2 + .../concordia-app/src/views/Home/index.jsx | 17 +++-- .../src/views/Topic/TopicCreate/index.jsx | 24 ++----- .../concordia-app/src/views/Topic/index.jsx | 2 + 7 files changed, 83 insertions(+), 92 deletions(-) diff --git a/packages/concordia-app/src/components/TopicList/TopicListRow/index.jsx b/packages/concordia-app/src/components/TopicList/TopicListRow/index.jsx index 2fea75c..fec1ce1 100644 --- a/packages/concordia-app/src/components/TopicList/TopicListRow/index.jsx +++ b/packages/concordia-app/src/components/TopicList/TopicListRow/index.jsx @@ -1,27 +1,44 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { + memo, useEffect, useMemo, useState, +} from 'react'; import { List } from 'semantic-ui-react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; -import AppContext from '../../AppContext'; import { FETCH_USER_DATABASE } from '../../../redux/actions/peerDbReplicationActions'; +import { breeze } from '../../../redux/store'; + +const { orbit } = breeze; const TopicListRow = (props) => { - const { topicData, topicId } = props; - const { breeze: { orbit } } = useContext(AppContext.Context); - const [topicSubject, setTopicSubject] = useState(); + const { id: topicId, topicCallHash } = props; + const getTopicResults = useSelector((state) => state.contracts.Forum.getTopic); + const [numberOfReplies, setNumberOfReplies] = useState(null); + const [username, setUsername] = useState(null); + const [topicAuthor, setTopicAuthor] = useState(null); + const [timestamp, setTimestamp] = useState(null); + const [topicSubject, setTopicSubject] = useState(null); const userAddress = useSelector((state) => state.user.address); const topics = useSelector((state) => state.orbitData.topics); const dispatch = useDispatch(); useEffect(() => { - if (userAddress !== topicData.userAddress) { + if (topicCallHash && getTopicResults[topicCallHash] !== undefined) { + setTopicAuthor(getTopicResults[topicCallHash].value[0]); + setUsername(getTopicResults[topicCallHash].value[1]); + setTimestamp(getTopicResults[topicCallHash].value[2] * 1000); + setNumberOfReplies(getTopicResults[topicCallHash].value[3].length); + } + }, [getTopicResults, topicCallHash]); + + useEffect(() => { + if (topicAuthor && userAddress !== topicAuthor) { dispatch({ type: FETCH_USER_DATABASE, orbit, - userAddress: topicData.userAddress, + userAddress: topicAuthor, }); } - }, [dispatch, orbit, topicData.userAddress, topicId, userAddress]); + }, [dispatch, topicAuthor, userAddress]); useEffect(() => { const topicFound = topics @@ -32,33 +49,28 @@ const TopicListRow = (props) => { } }, [topicId, topics]); - return ( + return useMemo(() => ( <> {topicSubject && topicSubject.subject} - {topicData.username} - {topicData.numberOfReplies} + {username} + {numberOfReplies} {' '} replies - timestamp + {timestamp} - ); + ), [topicSubject, username, numberOfReplies, timestamp]); }; -const TopicData = PropTypes.PropTypes.shape({ - userAddress: PropTypes.string.isRequired, - username: PropTypes.string.isRequired, - timestamp: PropTypes.number.isRequired, - numberOfReplies: PropTypes.number.isRequired, -}); - TopicListRow.propTypes = { - topicData: TopicData.isRequired, - topicId: PropTypes.number.isRequired, + id: PropTypes.number.isRequired, + topicCallHash: PropTypes.string.isRequired, }; -export default TopicListRow; +TopicListRow.whyDidYouRender = true; + +export default memo(TopicListRow); diff --git a/packages/concordia-app/src/components/TopicList/index.jsx b/packages/concordia-app/src/components/TopicList/index.jsx index 4450ccd..b7ac6c5 100644 --- a/packages/concordia-app/src/components/TopicList/index.jsx +++ b/packages/concordia-app/src/components/TopicList/index.jsx @@ -1,72 +1,57 @@ import React, { - useCallback, - useContext, useEffect, useMemo, useState, + useEffect, useMemo, useState, } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { List } from 'semantic-ui-react'; import { useHistory } from 'react-router'; -import AppContext from '../AppContext'; import TopicListRow from './TopicListRow'; import { PLACEHOLDER_TYPE_TOPIC } from '../../constants/PlaceholderTypes'; import Placeholder from '../Placeholder'; import './styles.css'; +import { drizzle } from '../../redux/store'; + +const { contracts: { Forum: { methods: { getTopic: { cacheCall: getTopicChainData } } } } } = drizzle; const TopicList = (props) => { const { topicIds } = props; - const { drizzle: { contracts: { Forum: { methods: { getTopic } } } } } = useContext(AppContext.Context); const [getTopicCallHashes, setGetTopicCallHashes] = useState([]); - const getTopicResults = useSelector((state) => state.contracts.Forum.getTopic); - const drizzleStatus = useSelector((state) => state.drizzleStatus); + const drizzleInitialized = useSelector((state) => state.drizzleStatus.initialized); + const drizzleInitializationFailed = useSelector((state) => state.drizzleStatus.failed); const history = useHistory(); useEffect(() => { - // TODO: is the drizzleStatus check necessary? - if (drizzleStatus.initialized && !drizzleStatus.failed) { - const newTopicPosted = topicIds - .some((topicId) => !getTopicCallHashes + if (drizzleInitialized && !drizzleInitializationFailed) { + const newTopicsFound = topicIds + .filter((topicId) => !getTopicCallHashes .map((getTopicCallHash) => getTopicCallHash.id) .includes(topicId)); - if (newTopicPosted) { - setGetTopicCallHashes(topicIds.map((topicId) => { - const foundGetTopicCallHash = getTopicCallHashes.find((getTopicCallHash) => getTopicCallHash.id === topicId); - - if (foundGetTopicCallHash !== undefined) { - return ({ ...foundGetTopicCallHash }); - } - - return ({ - id: topicId, - hash: getTopic.cacheCall(topicId), - }); - })); + if (newTopicsFound.length > 0) { + setGetTopicCallHashes([ + ...getTopicCallHashes, + ...newTopicsFound + .map((topicId) => ({ + id: topicId, + hash: getTopicChainData(topicId), + })), + ]); } } - }, [drizzleStatus.failed, drizzleStatus.initialized, getTopic, getTopicCallHashes, topicIds]); - - const handleTopicClick = useCallback((topicId) => { - history.push(`/topics/${topicId}`); - }, [history]); + }, [drizzleInitializationFailed, drizzleInitialized, getTopicCallHashes, topicIds]); const topics = useMemo(() => topicIds .map((topicId) => { - const getTopicHash = getTopicCallHashes.find((getTopicCallHash) => getTopicCallHash.id === topicId); + const topicHash = getTopicCallHashes.find((getTopicCallHash) => getTopicCallHash.id === topicId); - if (getTopicHash && getTopicResults[getTopicHash.hash] !== undefined) { - const topicData = { - userAddress: getTopicResults[getTopicHash.hash].value[0], - username: getTopicResults[getTopicHash.hash].value[1], - timestamp: getTopicResults[getTopicHash.hash].value[2] * 1000, - numberOfReplies: getTopicResults[getTopicHash.hash].value[3].length, - }; + const handleTopicClick = () => { + history.push(`/topics/${topicId}`); + }; + if (topicHash) { return ( - handleTopicClick(topicId)}> - + + ); } @@ -79,7 +64,7 @@ const TopicList = (props) => { /> ); - }), [getTopicCallHashes, getTopicResults, handleTopicClick, topicIds]); + }), [getTopicCallHashes, history, topicIds]); return ( @@ -92,4 +77,6 @@ TopicList.propTypes = { topicIds: PropTypes.arrayOf(PropTypes.number).isRequired, }; +TopicList.whyDidYouRender = true; + export default TopicList; diff --git a/packages/concordia-app/src/layouts/MainLayout/index.jsx b/packages/concordia-app/src/layouts/MainLayout/index.jsx index 0864457..6236c70 100644 --- a/packages/concordia-app/src/layouts/MainLayout/index.jsx +++ b/packages/concordia-app/src/layouts/MainLayout/index.jsx @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; - import MainLayoutMenu from './MainLayoutMenu'; const MainLayout = (props) => { diff --git a/packages/concordia-app/src/views/Home/Board/index.jsx b/packages/concordia-app/src/views/Home/Board/index.jsx index 7a8b074..8f8814f 100644 --- a/packages/concordia-app/src/views/Home/Board/index.jsx +++ b/packages/concordia-app/src/views/Home/Board/index.jsx @@ -46,4 +46,6 @@ Board.propTypes = { numberOfTopics: PropTypes.number.isRequired, }; +Board.whyDidYouRender = true; + export default Board; diff --git a/packages/concordia-app/src/views/Home/index.jsx b/packages/concordia-app/src/views/Home/index.jsx index a919f97..bb6d17c 100644 --- a/packages/concordia-app/src/views/Home/index.jsx +++ b/packages/concordia-app/src/views/Home/index.jsx @@ -1,31 +1,34 @@ import React, { - useContext, useEffect, useMemo, useState, + memo, useEffect, useMemo, useState, } from 'react'; import { Container } from 'semantic-ui-react'; import { useSelector } from 'react-redux'; -import AppContext from '../../components/AppContext'; import Board from './Board'; import './styles.css'; +import { drizzle } from '../../redux/store'; + +const { contracts: { Forum: { methods: { getNumberOfTopics } } } } = drizzle; const Home = () => { - const { drizzle: { contracts: { Forum: { methods: { getNumberOfTopics } } } } } = useContext(AppContext.Context); const [numberOfTopicsCallHash, setNumberOfTopicsCallHash] = useState(''); const getNumberOfTopicsResults = useSelector((state) => state.contracts.Forum.getNumberOfTopics); useEffect(() => { setNumberOfTopicsCallHash(getNumberOfTopics.cacheCall()); - }, [getNumberOfTopics]); + }, []); const numberOfTopics = useMemo(() => (getNumberOfTopicsResults[numberOfTopicsCallHash] !== undefined ? parseInt(getNumberOfTopicsResults[numberOfTopicsCallHash].value, 10) : null), [getNumberOfTopicsResults, numberOfTopicsCallHash]); - return ( + return useMemo(() => ( {numberOfTopics !== null && } - ); + ), [numberOfTopics]); }; -export default Home; +Home.whyDidYouRender = true; + +export default memo(Home); diff --git a/packages/concordia-app/src/views/Topic/TopicCreate/index.jsx b/packages/concordia-app/src/views/Topic/TopicCreate/index.jsx index 2870d1d..6b26c4a 100644 --- a/packages/concordia-app/src/views/Topic/TopicCreate/index.jsx +++ b/packages/concordia-app/src/views/Topic/TopicCreate/index.jsx @@ -1,5 +1,5 @@ import React, { - useCallback, useContext, useEffect, useState, + useCallback, useEffect, useState, } from 'react'; import { Button, Container, Form, Icon, Input, TextArea, @@ -7,30 +7,16 @@ import { import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router'; import { useSelector } from 'react-redux'; -import AppContext from '../../../components/AppContext'; import './styles.css'; +import { drizzle, breeze } from '../../../redux/store'; + +const { contracts: { Forum: { methods: { createTopic } } } } = drizzle; +const { orbit: { stores } } = breeze; const TopicCreate = (props) => { const { account } = props; - - const { - drizzle: { - contracts: { - Forum: { - methods: { createTopic }, - }, - }, - }, - breeze: { - orbit: { - stores, - }, - }, - } = useContext(AppContext.Context); - const transactionStack = useSelector((state) => state.transactionStack); const transactions = useSelector((state) => state.transactions); - const [subjectInput, setSubjectInput] = useState(''); const [messageInput, setMessageInput] = useState(''); const [topicSubjectInputEmptySubmit, setTopicSubjectInputEmptySubmit] = useState(false); diff --git a/packages/concordia-app/src/views/Topic/index.jsx b/packages/concordia-app/src/views/Topic/index.jsx index 0e1900f..6770b05 100644 --- a/packages/concordia-app/src/views/Topic/index.jsx +++ b/packages/concordia-app/src/views/Topic/index.jsx @@ -16,4 +16,6 @@ const Topic = () => { ); }; +Topic.whyDidYouRender = true; + export default Topic;