Browse Source

Use memoization to avoid unnecessary renders

develop
Apostolos Fanakis 4 years ago
parent
commit
1b18bd39aa
  1. 58
      packages/concordia-app/src/components/TopicList/TopicListRow/index.jsx
  2. 71
      packages/concordia-app/src/components/TopicList/index.jsx
  3. 1
      packages/concordia-app/src/layouts/MainLayout/index.jsx
  4. 2
      packages/concordia-app/src/views/Home/Board/index.jsx
  5. 17
      packages/concordia-app/src/views/Home/index.jsx
  6. 24
      packages/concordia-app/src/views/Topic/TopicCreate/index.jsx
  7. 2
      packages/concordia-app/src/views/Topic/index.jsx

58
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(() => (
<>
<List.Header>
<List.Icon name="right triangle" />
{topicSubject && topicSubject.subject}
</List.Header>
<List.Content>
{topicData.username}
{topicData.numberOfReplies}
{username}
{numberOfReplies}
{' '}
replies
timestamp
{timestamp}
</List.Content>
</>
);
), [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);

71
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 (
<List.Item key={topicId} className="list-item" name={topicId} onClick={() => handleTopicClick(topicId)}>
<TopicListRow
topicData={topicData}
topicId={topicId}
/>
<List.Item key={topicId} className="list-item" name={topicId} onClick={handleTopicClick}>
<TopicListRow id={topicId} topicCallHash={topicHash.hash} />
</List.Item>
);
}
@ -79,7 +64,7 @@ const TopicList = (props) => {
/>
</List.Item>
);
}), [getTopicCallHashes, getTopicResults, handleTopicClick, topicIds]);
}), [getTopicCallHashes, history, topicIds]);
return (
<List selection divided id="topic-list" size="big">
@ -92,4 +77,6 @@ TopicList.propTypes = {
topicIds: PropTypes.arrayOf(PropTypes.number).isRequired,
};
TopicList.whyDidYouRender = true;
export default TopicList;

1
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) => {

2
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;

17
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(() => (
<Container id="home-container" textAlign="center">
{numberOfTopics !== null && <Board numberOfTopics={numberOfTopics} />}
</Container>
);
), [numberOfTopics]);
};
export default Home;
Home.whyDidYouRender = true;
export default memo(Home);

24
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);

2
packages/concordia-app/src/views/Topic/index.jsx

@ -16,4 +16,6 @@ const Topic = () => {
);
};
Topic.whyDidYouRender = true;
export default Topic;

Loading…
Cancel
Save