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 { List } from 'semantic-ui-react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import AppContext from '../../AppContext';
import { FETCH_USER_DATABASE } from '../../../redux/actions/peerDbReplicationActions'; import { FETCH_USER_DATABASE } from '../../../redux/actions/peerDbReplicationActions';
import { breeze } from '../../../redux/store';
const { orbit } = breeze;
const TopicListRow = (props) => { const TopicListRow = (props) => {
const { topicData, topicId } = props; const { id: topicId, topicCallHash } = props;
const { breeze: { orbit } } = useContext(AppContext.Context); const getTopicResults = useSelector((state) => state.contracts.Forum.getTopic);
const [topicSubject, setTopicSubject] = useState(); 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 userAddress = useSelector((state) => state.user.address);
const topics = useSelector((state) => state.orbitData.topics); const topics = useSelector((state) => state.orbitData.topics);
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { 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({ dispatch({
type: FETCH_USER_DATABASE, type: FETCH_USER_DATABASE,
orbit, orbit,
userAddress: topicData.userAddress, userAddress: topicAuthor,
}); });
} }
}, [dispatch, orbit, topicData.userAddress, topicId, userAddress]); }, [dispatch, topicAuthor, userAddress]);
useEffect(() => { useEffect(() => {
const topicFound = topics const topicFound = topics
@ -32,33 +49,28 @@ const TopicListRow = (props) => {
} }
}, [topicId, topics]); }, [topicId, topics]);
return ( return useMemo(() => (
<> <>
<List.Header> <List.Header>
<List.Icon name="right triangle" /> <List.Icon name="right triangle" />
{topicSubject && topicSubject.subject} {topicSubject && topicSubject.subject}
</List.Header> </List.Header>
<List.Content> <List.Content>
{topicData.username} {username}
{topicData.numberOfReplies} {numberOfReplies}
{' '} {' '}
replies replies
timestamp {timestamp}
</List.Content> </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 = { TopicListRow.propTypes = {
topicData: TopicData.isRequired, id: PropTypes.number.isRequired,
topicId: 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, { import React, {
useCallback, useEffect, useMemo, useState,
useContext, useEffect, useMemo, useState,
} from 'react'; } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { List } from 'semantic-ui-react'; import { List } from 'semantic-ui-react';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import AppContext from '../AppContext';
import TopicListRow from './TopicListRow'; import TopicListRow from './TopicListRow';
import { PLACEHOLDER_TYPE_TOPIC } from '../../constants/PlaceholderTypes'; import { PLACEHOLDER_TYPE_TOPIC } from '../../constants/PlaceholderTypes';
import Placeholder from '../Placeholder'; import Placeholder from '../Placeholder';
import './styles.css'; import './styles.css';
import { drizzle } from '../../redux/store';
const { contracts: { Forum: { methods: { getTopic: { cacheCall: getTopicChainData } } } } } = drizzle;
const TopicList = (props) => { const TopicList = (props) => {
const { topicIds } = props; const { topicIds } = props;
const { drizzle: { contracts: { Forum: { methods: { getTopic } } } } } = useContext(AppContext.Context);
const [getTopicCallHashes, setGetTopicCallHashes] = useState([]); const [getTopicCallHashes, setGetTopicCallHashes] = useState([]);
const getTopicResults = useSelector((state) => state.contracts.Forum.getTopic); const drizzleInitialized = useSelector((state) => state.drizzleStatus.initialized);
const drizzleStatus = useSelector((state) => state.drizzleStatus); const drizzleInitializationFailed = useSelector((state) => state.drizzleStatus.failed);
const history = useHistory(); const history = useHistory();
useEffect(() => { useEffect(() => {
// TODO: is the drizzleStatus check necessary? if (drizzleInitialized && !drizzleInitializationFailed) {
if (drizzleStatus.initialized && !drizzleStatus.failed) { const newTopicsFound = topicIds
const newTopicPosted = topicIds .filter((topicId) => !getTopicCallHashes
.some((topicId) => !getTopicCallHashes
.map((getTopicCallHash) => getTopicCallHash.id) .map((getTopicCallHash) => getTopicCallHash.id)
.includes(topicId)); .includes(topicId));
if (newTopicPosted) { if (newTopicsFound.length > 0) {
setGetTopicCallHashes(topicIds.map((topicId) => { setGetTopicCallHashes([
const foundGetTopicCallHash = getTopicCallHashes.find((getTopicCallHash) => getTopicCallHash.id === topicId); ...getTopicCallHashes,
...newTopicsFound
if (foundGetTopicCallHash !== undefined) { .map((topicId) => ({
return ({ ...foundGetTopicCallHash }); id: topicId,
} hash: getTopicChainData(topicId),
})),
return ({ ]);
id: topicId,
hash: getTopic.cacheCall(topicId),
});
}));
} }
} }
}, [drizzleStatus.failed, drizzleStatus.initialized, getTopic, getTopicCallHashes, topicIds]); }, [drizzleInitializationFailed, drizzleInitialized, getTopicCallHashes, topicIds]);
const handleTopicClick = useCallback((topicId) => {
history.push(`/topics/${topicId}`);
}, [history]);
const topics = useMemo(() => topicIds const topics = useMemo(() => topicIds
.map((topicId) => { .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 handleTopicClick = () => {
const topicData = { history.push(`/topics/${topicId}`);
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,
};
if (topicHash) {
return ( return (
<List.Item key={topicId} className="list-item" name={topicId} onClick={() => handleTopicClick(topicId)}> <List.Item key={topicId} className="list-item" name={topicId} onClick={handleTopicClick}>
<TopicListRow <TopicListRow id={topicId} topicCallHash={topicHash.hash} />
topicData={topicData}
topicId={topicId}
/>
</List.Item> </List.Item>
); );
} }
@ -79,7 +64,7 @@ const TopicList = (props) => {
/> />
</List.Item> </List.Item>
); );
}), [getTopicCallHashes, getTopicResults, handleTopicClick, topicIds]); }), [getTopicCallHashes, history, topicIds]);
return ( return (
<List selection divided id="topic-list" size="big"> <List selection divided id="topic-list" size="big">
@ -92,4 +77,6 @@ TopicList.propTypes = {
topicIds: PropTypes.arrayOf(PropTypes.number).isRequired, topicIds: PropTypes.arrayOf(PropTypes.number).isRequired,
}; };
TopicList.whyDidYouRender = true;
export default TopicList; export default TopicList;

1
packages/concordia-app/src/layouts/MainLayout/index.jsx

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import MainLayoutMenu from './MainLayoutMenu'; import MainLayoutMenu from './MainLayoutMenu';
const MainLayout = (props) => { const MainLayout = (props) => {

2
packages/concordia-app/src/views/Home/Board/index.jsx

@ -46,4 +46,6 @@ Board.propTypes = {
numberOfTopics: PropTypes.number.isRequired, numberOfTopics: PropTypes.number.isRequired,
}; };
Board.whyDidYouRender = true;
export default Board; export default Board;

17
packages/concordia-app/src/views/Home/index.jsx

@ -1,31 +1,34 @@
import React, { import React, {
useContext, useEffect, useMemo, useState, memo, useEffect, useMemo, useState,
} from 'react'; } from 'react';
import { Container } from 'semantic-ui-react'; import { Container } from 'semantic-ui-react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import AppContext from '../../components/AppContext';
import Board from './Board'; import Board from './Board';
import './styles.css'; import './styles.css';
import { drizzle } from '../../redux/store';
const { contracts: { Forum: { methods: { getNumberOfTopics } } } } = drizzle;
const Home = () => { const Home = () => {
const { drizzle: { contracts: { Forum: { methods: { getNumberOfTopics } } } } } = useContext(AppContext.Context);
const [numberOfTopicsCallHash, setNumberOfTopicsCallHash] = useState(''); const [numberOfTopicsCallHash, setNumberOfTopicsCallHash] = useState('');
const getNumberOfTopicsResults = useSelector((state) => state.contracts.Forum.getNumberOfTopics); const getNumberOfTopicsResults = useSelector((state) => state.contracts.Forum.getNumberOfTopics);
useEffect(() => { useEffect(() => {
setNumberOfTopicsCallHash(getNumberOfTopics.cacheCall()); setNumberOfTopicsCallHash(getNumberOfTopics.cacheCall());
}, [getNumberOfTopics]); }, []);
const numberOfTopics = useMemo(() => (getNumberOfTopicsResults[numberOfTopicsCallHash] !== undefined const numberOfTopics = useMemo(() => (getNumberOfTopicsResults[numberOfTopicsCallHash] !== undefined
? parseInt(getNumberOfTopicsResults[numberOfTopicsCallHash].value, 10) ? parseInt(getNumberOfTopicsResults[numberOfTopicsCallHash].value, 10)
: null), : null),
[getNumberOfTopicsResults, numberOfTopicsCallHash]); [getNumberOfTopicsResults, numberOfTopicsCallHash]);
return ( return useMemo(() => (
<Container id="home-container" textAlign="center"> <Container id="home-container" textAlign="center">
{numberOfTopics !== null && <Board numberOfTopics={numberOfTopics} />} {numberOfTopics !== null && <Board numberOfTopics={numberOfTopics} />}
</Container> </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, { import React, {
useCallback, useContext, useEffect, useState, useCallback, useEffect, useState,
} from 'react'; } from 'react';
import { import {
Button, Container, Form, Icon, Input, TextArea, Button, Container, Form, Icon, Input, TextArea,
@ -7,30 +7,16 @@ import {
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import AppContext from '../../../components/AppContext';
import './styles.css'; import './styles.css';
import { drizzle, breeze } from '../../../redux/store';
const { contracts: { Forum: { methods: { createTopic } } } } = drizzle;
const { orbit: { stores } } = breeze;
const TopicCreate = (props) => { const TopicCreate = (props) => {
const { account } = props; const { account } = props;
const {
drizzle: {
contracts: {
Forum: {
methods: { createTopic },
},
},
},
breeze: {
orbit: {
stores,
},
},
} = useContext(AppContext.Context);
const transactionStack = useSelector((state) => state.transactionStack); const transactionStack = useSelector((state) => state.transactionStack);
const transactions = useSelector((state) => state.transactions); const transactions = useSelector((state) => state.transactions);
const [subjectInput, setSubjectInput] = useState(''); const [subjectInput, setSubjectInput] = useState('');
const [messageInput, setMessageInput] = useState(''); const [messageInput, setMessageInput] = useState('');
const [topicSubjectInputEmptySubmit, setTopicSubjectInputEmptySubmit] = useState(false); 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; export default Topic;

Loading…
Cancel
Save