diff --git a/src/components/Feed/Feed.jsx b/src/components/Feed/Feed.jsx index 2a4b09e33..000fa8abc 100644 --- a/src/components/Feed/Feed.jsx +++ b/src/components/Feed/Feed.jsx @@ -82,6 +82,11 @@ class Feed extends React.Component { } } + shouldComponentUpdate(nextProps) { + // avoid re-rendering of this heavy component if no properties are changed + return !_.isEqual(this.props, nextProps) + } + render() { const { id, user, currentUser, topicMessage, totalComments, hasMoreComments, onLoadMoreComments, isLoadingComments, diff --git a/src/components/RichTextArea/RichTextArea.jsx b/src/components/RichTextArea/RichTextArea.jsx index 733e873d1..fe9f9354f 100644 --- a/src/components/RichTextArea/RichTextArea.jsx +++ b/src/components/RichTextArea/RichTextArea.jsx @@ -55,7 +55,15 @@ const blocks = [ class RichTextArea extends React.Component { constructor(props) { super(props) - this.state = {editorExpanded: false, editorState: EditorState.createEmpty(), titleValue: '', suggestions: [], allSuggestions:[], isPrivate: false} + this.state = { + editorExpanded: false, + editorState: EditorState.createEmpty(), + titleValue: '', + suggestions: [], + allSuggestions:[], + isPrivate: false + } + this.onTitleChange = this.onTitleChange.bind(this) this.onEditorChange = this.onEditorChange.bind(this) this.handleKeyCommand = this.handleKeyCommand.bind(this) @@ -139,14 +147,17 @@ class RichTextArea extends React.Component { do { if (currNode.className && currNode.className.indexOf - && currNode.className.indexOf('btn-close') > -1) { + && currNode.className.indexOf('btn-close') > -1 + ) { isCloseButton = true } if (currNode.className && currNode.className.indexOf - && currNode.className.indexOf('btn-close-creat') > -1) { - isCloseButton = true, + && currNode.className.indexOf('btn-close-creat') > -1 + ) { + isCloseButton = true + this.setState({ titleValue: '', editorState: EditorState.createEmpty(), @@ -170,7 +181,17 @@ class RichTextArea extends React.Component { if (!isEditor && !isCloseButton && hasContent) { return } - this.setState({editorExpanded: isEditor && !isCloseButton, isPrivate: isEditor && !isCloseButton ? this.state.isPrivate : false}) + + const editorExpanded = isEditor && !isCloseButton + const isPrivate = isEditor && !isCloseButton ? this.state.isPrivate : false + + // to avoid unnecessary re-rendering on every click, only update state if any of the values is updated + if (editorExpanded !== this.state.editorExpanded || isPrivate !== this.state.isPrivate) { + this.setState({ + editorExpanded, + isPrivate, + }) + } } handleKeyCommand(command) { diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js index a2ad10de6..d06962793 100644 --- a/src/projects/detail/containers/FeedContainer.js +++ b/src/projects/detail/containers/FeedContainer.js @@ -27,10 +27,10 @@ import PostsRefreshPrompt from '../components/PostsRefreshPrompt' import MediaQuery from 'react-responsive' import ChatButton from '../../../components/ChatButton/ChatButton' import NewPostMobile from '../../../components/Feed/NewPostMobile' -import ScrollableFeed from '../../../components/Feed/ScrollableFeed' import FullscreenFeedContainer from '../containers/FullscreenFeedContainer' import Section from '../components/Section' import SectionTitle from '../components/SectionTitle' +import SingleFeedContainer from './SingleFeedContainer' import { scrollToHash } from '../../../components/ScrollToAnchors' import { isSystemUser } from '../../../helpers/tcHelpers' @@ -403,25 +403,24 @@ class FeedView extends React.Component { this.enterFullscreen(feed.id) }} > - {feeds.map((feed) => (
-
diff --git a/src/projects/detail/containers/SingleFeedContainer.jsx b/src/projects/detail/containers/SingleFeedContainer.jsx new file mode 100644 index 000000000..3835c9907 --- /dev/null +++ b/src/projects/detail/containers/SingleFeedContainer.jsx @@ -0,0 +1,59 @@ +/** + * The main purpose of this component is to bind methods which require feed.id + * so that we don't re-create them on every render like: + * onNewCommentChange: onNewCommentChange.bind(this, feed.id) + * So inside Feed component we can use shouldComponentUpdate to compare properties + * and only render if properties are changed. + * + * If we define methods like this + * onNewCommentChange: onNewCommentChange.bind(this, feed.id) + * then such function would be recreated on every render and shouldComponentUpdate + * will return true for every render. + */ +import React from 'react' +import _ from 'lodash' + +import ScrollableFeed from '../../../components/Feed/ScrollableFeed' + +const bindMethods = [ + 'onNewCommentChange', + 'onAddNewComment', + 'onLoadMoreComments', + 'onEditMessage', + 'onSaveMessageChange', + 'onSaveMessage', + 'onDeleteMessage', + 'onEditTopic', + 'onTopicChange', + 'onSaveTopic', + 'onDeleteTopic', + 'onEnterFullscreenClick', +] + +class SingleFeedContainer extends React.Component { + constructor(props) { + super(props) + + bindMethods.forEach((method) => { + this[method] = () => this.props[method](this.props.id) + }) + } + + render() { + const nonBindProps = _.omit(this.props, bindMethods) + const bindProps = _.zipObject(bindMethods, bindMethods.map((method) => this[method])) + + return ( + + ) + } +} + +export default SingleFeedContainer \ No newline at end of file