Remove function binds in render wherever possible, use PureComponent

release
Eugen Rochko 2017-09-29 16:46:29 +02:00
parent 648823c761
commit fdb88daa31
7 changed files with 153 additions and 114 deletions

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import * as SVGs from '../svgs'
export default class Anchors extends React.Component {
export default class Anchors extends React.PureComponent {
constructor(props) {
super(props)
@ -14,6 +14,15 @@ export default class Anchors extends React.Component {
this.state = {
selected: defaultCategory.name
}
this.handleClick = this.handleClick.bind(this)
}
handleClick (e) {
var index = e.currentTarget.getAttribute('data-index')
var { categories, onAnchorClick } = this.props
onAnchorClick(categories[index], index)
}
render() {
@ -34,7 +43,8 @@ export default class Anchors extends React.Component {
<span
key={name}
title={i18n.categories[name.toLowerCase()]}
onClick={() => onAnchorClick(category, i)}
data-index={i}
onClick={this.handleClick}
className={`emoji-mart-anchor ${isSelected ? 'emoji-mart-anchor-selected' : ''}`}
style={{ color: isSelected ? color : null }}
>

View File

@ -5,6 +5,13 @@ import frequently from '../utils/frequently'
import { Emoji } from '.'
export default class Category extends React.Component {
constructor(props) {
super(props)
this.setContainerRef = this.setContainerRef.bind(this)
this.setLabelRef = this.setLabelRef.bind(this)
}
componentDidMount() {
this.parent = this.container.parentNode
@ -133,29 +140,26 @@ export default class Category extends React.Component {
}
}
return <div ref={this.setContainerRef.bind(this)} className={`emoji-mart-category ${emojis && !emojis.length ? 'emoji-mart-no-results' : ''}`} style={containerStyles}>
return <div ref={this.setContainerRef} className={`emoji-mart-category ${emojis && !emojis.length ? 'emoji-mart-no-results' : ''}`} style={containerStyles}>
<div style={labelStyles} data-name={name} className='emoji-mart-category-label'>
<span style={labelSpanStyles} ref={this.setLabelRef.bind(this)}>{i18n.categories[name.toLowerCase()]}</span>
<span style={labelSpanStyles} ref={this.setLabelRef}>{i18n.categories[name.toLowerCase()]}</span>
</div>
{emojis && emojis.map((emoji) =>
Emoji({
emoji: emoji,
...emojiProps
})
<Emoji emoji={emoji} {...emojiProps} />
)}
{emojis && !emojis.length &&
<div>
<div>
{Emoji({
...emojiProps,
size: 38,
emoji: 'sleuth_or_spy',
onOver: null,
onLeave: null,
onClick: null,
})}
<Emoji
{...emojiProps}
size={38}
emoji='sleuth_or_spy'
onOver={null}
onLeave={null}
onClick={null}
/>
</div>
<div className='emoji-mart-no-results-label'>

View File

@ -23,87 +23,98 @@ const _getSanitizedData = (props) => {
return getSanitizedData(emoji, skin, set)
}
const _handleClick = (e, props) => {
if (!props.onClick) { return }
var { onClick } = props,
emoji = _getSanitizedData(props)
export default class Emoji extends React.PureComponent {
onClick(emoji, e)
}
constructor (props) {
super(props)
const _handleOver = (e, props) => {
if (!props.onOver) { return }
var { onOver } = props,
emoji = _getSanitizedData(props)
onOver(emoji, e)
}
const _handleLeave = (e, props) => {
if (!props.onLeave) { return }
var { onLeave } = props,
emoji = _getSanitizedData(props)
onLeave(emoji, e)
}
const Emoji = (props) => {
for (let k in Emoji.defaultProps) {
if (props[k] == undefined && Emoji.defaultProps[k] != undefined) {
props[k] = Emoji.defaultProps[k]
}
this.handleClick = this.handleClick.bind(this)
this.handleEnter = this.handleEnter.bind(this)
this.handleLeave = this.handleLeave.bind(this)
}
var { unified, custom, imageUrl } = _getData(props),
style = {},
children = props.children
handleClick (e) {
if (!this.props.onClick) { return }
if (!unified && !custom) {
return null
const { onClick } = this.props,
emoji = _getSanitizedData(this.props)
onClick(emoji, e)
}
if (props.native && unified) {
style = { fontSize: props.size }
children = unifiedToNative(unified)
handleEnter (e) {
if (!this.props.onOver) { return }
if (props.forceSize) {
style.display = 'inline-block'
style.width = props.size
style.height = props.size
}
} else if (custom) {
style = {
width: props.size,
height: props.size,
display: 'inline-block',
backgroundImage: `url(${imageUrl})`,
backgroundSize: '100%',
}
} else {
let setHasEmoji = _getData(props)[`has_img_${props.set}`]
const { onOver } = this.props,
emoji = _getSanitizedData(this.props)
if (!setHasEmoji) {
onOver(emoji, e)
}
handleLeave (e) {
if (!this.props.onLeave) { return }
const { onLeave } = this.props,
emoji = _getSanitizedData(this.props)
onLeave(emoji, e)
}
render () {
var props = this.props;
var { unified, custom, imageUrl } = _getData(props),
style = {},
children = props.children
if (!unified && !custom) {
return null
}
style = {
width: props.size,
height: props.size,
display: 'inline-block',
backgroundImage: `url(${props.backgroundImageFn(props.set, props.sheetSize)})`,
backgroundSize: `${100 * SHEET_COLUMNS}%`,
backgroundPosition: _getPosition(props),
if (props.native && unified) {
style = { fontSize: props.size }
children = unifiedToNative(unified)
if (props.forceSize) {
style.display = 'inline-block'
style.width = props.size
style.height = props.size
}
} else if (custom) {
style = {
width: props.size,
height: props.size,
display: 'inline-block',
backgroundImage: `url(${imageUrl})`,
backgroundSize: '100%',
}
} else {
let setHasEmoji = _getData(props)[`has_img_${props.set}`]
if (!setHasEmoji) {
return null
}
style = {
width: props.size,
height: props.size,
display: 'inline-block',
backgroundImage: `url(${props.backgroundImageFn(props.set, props.sheetSize)})`,
backgroundSize: `${100 * SHEET_COLUMNS}%`,
backgroundPosition: _getPosition(props),
}
}
return <span
key={props.emoji.id || props.emoji}
onClick={this.handleClick}
onMouseEnter={this.handleEnter}
onMouseLeave={this.handleLeave}
className='emoji-mart-emoji'>
<span style={style}>{children}</span>
</span>
}
return <span
key={props.emoji.id || props.emoji}
onClick={(e) => _handleClick(e, props)}
onMouseEnter={(e) => _handleOver(e, props)}
onMouseLeave={(e) => _handleLeave(e, props)}
className='emoji-mart-emoji'>
<span style={style}>{children}</span>
</span>
}
Emoji.propTypes = {
@ -134,5 +145,3 @@ Emoji.defaultProps = {
onLeave: (() => {}),
onClick: (() => {}),
}
export default Emoji

View File

@ -33,7 +33,7 @@ const I18N = {
},
}
export default class Picker extends React.Component {
export default class Picker extends React.PureComponent {
constructor(props) {
super(props)
@ -116,6 +116,19 @@ export default class Picker extends React.Component {
}
this.categories.unshift(SEARCH_CATEGORY)
this.setAnchorsRef = this.setAnchorsRef.bind(this)
this.handleAnchorClick = this.handleAnchorClick.bind(this)
this.setSearchRef = this.setSearchRef.bind(this)
this.handleSearch = this.handleSearch.bind(this)
this.setScrollRef = this.setScrollRef.bind(this)
this.handleScroll = this.handleScroll.bind(this)
this.handleScrollPaint = this.handleScrollPaint.bind(this)
this.handleEmojiOver = this.handleEmojiOver.bind(this)
this.handleEmojiLeave = this.handleEmojiLeave.bind(this)
this.handleEmojiClick = this.handleEmojiClick.bind(this)
this.setPreviewRef = this.setPreviewRef.bind(this)
this.handleSkinChange = this.handleSkinChange.bind(this)
}
componentWillReceiveProps(props) {
@ -206,7 +219,7 @@ export default class Picker extends React.Component {
handleScroll() {
if (!this.waitingForPaint) {
this.waitingForPaint = true
window.requestAnimationFrame(this.handleScrollPaint.bind(this))
window.requestAnimationFrame(this.handleScrollPaint)
}
}
@ -368,17 +381,17 @@ export default class Picker extends React.Component {
return <div style={{width: width, ...style}} className='emoji-mart'>
<div className='emoji-mart-bar'>
<Anchors
ref={this.setAnchorsRef.bind(this)}
ref={this.setAnchorsRef}
i18n={this.i18n}
color={color}
categories={this.categories}
onAnchorClick={this.handleAnchorClick.bind(this)}
onAnchorClick={this.handleAnchorClick}
/>
</div>
<Search
ref={this.setSearchRef.bind(this)}
onSearch={this.handleSearch.bind(this)}
ref={this.setSearchRef}
onSearch={this.handleSearch}
i18n={this.i18n}
emojisToShowFilter={emojisToShowFilter}
include={include}
@ -387,7 +400,7 @@ export default class Picker extends React.Component {
autoFocus={autoFocus}
/>
<div ref={this.setScrollRef.bind(this)} className='emoji-mart-scroll' onScroll={this.handleScroll.bind(this)}>
<div ref={this.setScrollRef} className='emoji-mart-scroll' onScroll={this.handleScroll}>
{this.getCategories().map((category, i) => {
return <Category
ref={this.setCategoryRef.bind(this, `category-${i}`)}
@ -407,9 +420,9 @@ export default class Picker extends React.Component {
sheetSize: sheetSize,
forceSize: native,
backgroundImageFn: backgroundImageFn,
onOver: this.handleEmojiOver.bind(this),
onLeave: this.handleEmojiLeave.bind(this),
onClick: this.handleEmojiClick.bind(this),
onOver: this.handleEmojiOver,
onLeave: this.handleEmojiLeave,
onClick: this.handleEmojiClick,
}}
/>
})}
@ -417,7 +430,7 @@ export default class Picker extends React.Component {
{showPreview && <div className='emoji-mart-bar'>
<Preview
ref={this.setPreviewRef.bind(this)}
ref={this.setPreviewRef}
title={title}
emoji={emoji}
emojiProps={{
@ -430,7 +443,7 @@ export default class Picker extends React.Component {
}}
skinsProps={{
skin: skin,
onChange: this.handleSkinChange.bind(this)
onChange: this.handleSkinChange
}}
/>
</div>}

View File

@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
import { Emoji, Skins } from '.'
import { getData } from '../utils'
export default class Preview extends React.Component {
export default class Preview extends React.PureComponent {
constructor(props) {
super(props)
this.state = { emoji: null }
@ -31,11 +31,7 @@ export default class Preview extends React.Component {
return <div className='emoji-mart-preview'>
<div className='emoji-mart-preview-emoji'>
{Emoji({
key: emoji.id,
emoji: emoji,
...emojiProps,
})}
<Emoji key={emoji.id} emoji={emoji} {...emojiProps} />
</div>
<div className='emoji-mart-preview-data'>
@ -55,10 +51,7 @@ export default class Preview extends React.Component {
} else {
return <div className='emoji-mart-preview'>
<div className='emoji-mart-preview-emoji'>
{idleEmoji && idleEmoji.length && Emoji({
emoji: idleEmoji,
...emojiProps,
})}
{idleEmoji && idleEmoji.length && <Emoji emoji={idleEmoji} {...emojiProps} />}
</div>
<div className='emoji-mart-preview-data'>

View File

@ -2,7 +2,14 @@ import React from 'react'
import PropTypes from 'prop-types'
import emojiIndex from '../utils/emoji-index'
export default class Search extends React.Component {
export default class Search extends React.PureComponent {
constructor (props) {
super(props)
this.setRef = this.setRef.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange() {
var value = this.input.value
@ -28,9 +35,9 @@ export default class Search extends React.Component {
return <div className='emoji-mart-search'>
<input
ref={this.setRef.bind(this)}
ref={this.setRef}
type='text'
onChange={this.handleChange.bind(this)}
onChange={this.handleChange}
placeholder={i18n.search}
autoFocus={autoFocus}
/>

View File

@ -1,16 +1,19 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class Skins extends React.Component {
export default class Skins extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
opened: false,
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(skin) {
handleClick(e) {
var skin = e.currentTarget.getAttribute('data-skin')
var { onChange } = this.props
if (!this.state.opened) {
@ -39,9 +42,9 @@ export default class Skins extends React.Component {
className={`emoji-mart-skin-swatch ${selected ? 'emoji-mart-skin-swatch-selected' : ''}`}
>
<span
onClick={() => this.handleClick(skinTone)}
className={`emoji-mart-skin emoji-mart-skin-tone-${skinTone}`}>
</span>
onClick={this.handleClick}
data-skin={skinTone}
className={`emoji-mart-skin emoji-mart-skin-tone-${skinTone}`} />
</span>
)
}