Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/components/Feed/Feed.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react'
import _ from 'lodash'
import cn from 'classnames'
import moment from 'moment'
import PropTypes from 'prop-types'
Expand Down Expand Up @@ -82,6 +83,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,
Expand Down
31 changes: 26 additions & 5 deletions src/components/RichTextArea/RichTextArea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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(),
Expand All @@ -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) {
Expand Down
59 changes: 30 additions & 29 deletions src/projects/detail/containers/FeedContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -58,6 +58,10 @@ class FeedView extends React.Component {
this.onNewPostChange = this.onNewPostChange.bind(this)
this.onEditMessage = this.onEditMessage.bind(this)
this.onSaveMessageChange = this.onSaveMessageChange.bind(this)
this.onSaveMessage = this.onSaveMessage.bind(this)
this.onDeleteMessage = this.onDeleteMessage.bind(this)
this.onSaveTopic = this.onSaveTopic.bind(this)
this.onDeleteTopic = this.onDeleteTopic.bind(this)
this.onEditTopic = this.onEditTopic.bind(this)
this.onTopicChange = this.onTopicChange.bind(this)
this.onRefreshFeeds = this.onRefreshFeeds.bind(this)
Expand Down Expand Up @@ -403,25 +407,24 @@ class FeedView extends React.Component {
this.enterFullscreen(feed.id)
}}
>
<ScrollableFeed
<SingleFeedContainer
{...{
...fullscreenFeed,
id: fullscreenFeedId,
allowComments: fullscreenFeed.allowComments && !!currentMemberRole,
currentUser,
allMembers,
projectMembers,
onNewCommentChange: this.onNewCommentChange.bind(this, fullscreenFeedId),
onAddNewComment: this.onAddNewComment.bind(this, fullscreenFeedId),
onLoadMoreComments: this.onShowAllComments.bind(this, fullscreenFeedId),
onEditMessage: this.onEditMessage.bind(this, fullscreenFeedId),
onSaveMessageChange: this.onSaveMessageChange.bind(this, fullscreenFeedId),
onSaveMessage: this.onSaveMessage.bind(this, fullscreenFeedId),
onDeleteMessage: this.onDeleteMessage.bind(this, fullscreenFeedId),
onEditTopic: this.onEditTopic.bind(this, fullscreenFeedId),
onTopicChange: this.onTopicChange.bind(this, fullscreenFeedId),
onSaveTopic: this.onSaveTopic.bind(this, fullscreenFeedId),
onDeleteTopic: this.onDeleteTopic.bind(this, fullscreenFeedId),
onNewCommentChange: this.onNewCommentChange,
onAddNewComment: this.onAddNewComment,
onLoadMoreComments: this.onShowAllComments,
onEditMessage: this.onEditMessage,
onSaveMessageChange: this.onSaveMessageChange,
onSaveMessage: this.onSaveMessage,
onDeleteMessage: this.onDeleteMessage,
onEditTopic: this.onEditTopic,
onTopicChange: this.onTopicChange,
onSaveTopic: this.onSaveTopic,
onDeleteTopic: this.onDeleteTopic,
onExitFullscreenClick: this.exitFullscreen,
isFullScreen: true,
}}
Expand Down Expand Up @@ -464,27 +467,25 @@ class FeedView extends React.Component {
</MediaQuery>
{feeds.map((feed) => (
<div styleName="feed-card" key={feed.id}>
<ScrollableFeed
<SingleFeedContainer
{...{
...feed,
id: feed.id.toString(), // convert it to string for `ScrollElement`
topicId: feed.id, // add topic id as a number, for `Feed`
allowComments: feed.allowComments && !!currentMemberRole,
currentUser,
allMembers,
projectMembers,
onNewCommentChange: this.onNewCommentChange.bind(this, feed.id),
onAddNewComment: this.onAddNewComment.bind(this, feed.id),
onLoadMoreComments: this.onShowAllComments.bind(this, feed.id),
onEditMessage: this.onEditMessage.bind(this, feed.id),
onSaveMessageChange: this.onSaveMessageChange.bind(this, feed.id),
onSaveMessage: this.onSaveMessage.bind(this, feed.id),
onDeleteMessage: this.onDeleteMessage.bind(this, feed.id),
onEditTopic: this.onEditTopic.bind(this, feed.id),
onTopicChange: this.onTopicChange.bind(this, feed.id),
onSaveTopic: this.onSaveTopic.bind(this, feed.id),
onDeleteTopic: this.onDeleteTopic.bind(this, feed.id),
onEnterFullscreenClick: this.enterFullscreen.bind(this, feed.id),
onNewCommentChange: this.onNewCommentChange,
onAddNewComment: this.onAddNewComment,
onLoadMoreComments: this.onShowAllComments,
onEditMessage: this.onEditMessage,
onSaveMessageChange: this.onSaveMessageChange,
onSaveMessage: this.onSaveMessage,
onDeleteMessage: this.onDeleteMessage,
onEditTopic: this.onEditTopic,
onTopicChange: this.onTopicChange,
onSaveTopic: this.onSaveTopic,
onDeleteTopic: this.onDeleteTopic,
onEnterFullscreenClick: this.enterFullscreen,
}}
/>
</div>
Expand Down
63 changes: 63 additions & 0 deletions src/projects/detail/containers/SingleFeedContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* 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) => {
// We cannot use an arrow function here, as later other component can apply .bind to these methods
this[method] = function() {
// we cannot use .bind here as we already bound "this" in these methods to another React component and we don't want to override it
return this.props[method](...[this.props.id, ...arguments])
}.bind(this)
})
}

render() {
const nonBindProps = _.omit(this.props, bindMethods)
const bindProps = _.zipObject(bindMethods, bindMethods.map((method) => this[method]))

return (
<ScrollableFeed
{...{
...nonBindProps,
...bindProps,
id: nonBindProps.id.toString(), // convert it to string for `ScrollElement`
topicId: nonBindProps.id, // add topic id as a number, for `Feed`
}}
/>
)
}
}

export default SingleFeedContainer