Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize things a little bit #1

Merged
merged 7 commits into from Apr 13, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .babelrc
@@ -1,5 +1,6 @@
{
"presets": ["es2015", "react"],
"plugins": ["transform-object-rest-spread"],
"env": {
"development": {
"presets": ["react-hmre"]
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
@@ -1,3 +1,3 @@
dist/
dist/*.js
npm-debug.log
node_modules/
7 changes: 6 additions & 1 deletion actions/index.js
@@ -1,7 +1,8 @@
import * as types from '../constants/ActionTypes'

let nextId = 0;
export function addTodo(text) {
return { type: types.ADD_TODO, text }
return { type: types.ADD_TODO, text, id: (nextId++).toString() }
}

export function deleteTodo(id) {
Expand All @@ -23,3 +24,7 @@ export function completeAll() {
export function clearCompleted() {
return { type: types.CLEAR_COMPLETED }
}

export function setFilter(filter) {
return { type: types.SET_FILTER, filter }
}
13 changes: 13 additions & 0 deletions components/App.js
@@ -0,0 +1,13 @@
import React, { PropTypes } from 'react'
import Header from '../components/Header'
import MainSection from '../components/MainSection'
import * as TodoActions from '../actions'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TodoActions import is useless here, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, missed this one. Doesn't affect the perf though.


const App = () => (
<div>
<Header />
<MainSection />
</div>
)

export default App
32 changes: 32 additions & 0 deletions components/FilterLink.js
@@ -0,0 +1,32 @@
import React from 'react'
import { connect } from 'react-redux'
import classnames from 'classnames'
import { setFilter } from '../actions'
import { SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED } from '../constants/TodoFilters'

const FILTER_TITLES = {
[SHOW_ALL]: 'All',
[SHOW_ACTIVE]: 'Active',
[SHOW_COMPLETED]: 'Completed'
}

const FilterLink = ({ filter, selected, onClick }) => (
<a className={classnames({ selected })}
style={{ cursor: 'pointer' }}
onClick={onClick}>
{FILTER_TITLES[filter]}
</a>
)

const mapStateToProps = (state, ownProps) => ({
selected: state.filter === ownProps.filter
})

const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setFilter(ownProps.filter))
})

export default connect(
mapStateToProps,
mapDispatchToProps
)(FilterLink)
121 changes: 54 additions & 67 deletions components/Footer.js
@@ -1,73 +1,60 @@
import React, { PropTypes, Component } from 'react'
import classnames from 'classnames'
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import FilterLink from './FilterLink'
import { getCompletedCount, getListedCount } from '../reducers'
import { clearCompleted } from '../actions'
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'

const FILTER_TITLES = {
[SHOW_ALL]: 'All',
[SHOW_ACTIVE]: 'Active',
[SHOW_COMPLETED]: 'Completed'
}

class Footer extends Component {
renderTodoCount() {
const { activeCount } = this.props
const itemWord = activeCount === 1 ? 'item' : 'items'

return (
<span className="todo-count">
<strong>{activeCount || 'No'}</strong> {itemWord} left
</span>
)
}

renderFilterLink(filter) {
const title = FILTER_TITLES[filter]
const { filter: selectedFilter, onShow } = this.props

return (
<a className={classnames({ selected: filter === selectedFilter })}
style={{ cursor: 'pointer' }}
onClick={() => onShow(filter)}>
{title}
</a>
)
}

renderClearButton() {
const { completedCount, onClearCompleted } = this.props
if (completedCount > 0) {
return (
<button className="clear-completed"
onClick={onClearCompleted} >
Clear completed
</button>
)
}
}

render() {
return (
<footer className="footer">
{this.renderTodoCount()}
<ul className="filters">
{[ SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED ].map(filter =>
<li key={filter}>
{this.renderFilterLink(filter)}
</li>
)}
</ul>
{this.renderClearButton()}
</footer>
)
}
}

const TodoCount = ({ activeCount }) => (
<span className="todo-count">
<strong>{activeCount || 'No'}</strong>
{' '}
{activeCount === 1 ? 'item' : 'items'} left
</span>
)

const ClearButton = ({ completedCount, clearCompleted }) => (
<button className="clear-completed"
onClick={clearCompleted} >
Clear completed
</button>
)

const Footer = ({ filter, completedCount, listedCount, clearCompleted }) => (
listedCount ? (
<footer className="footer">
<TodoCount activeCount={listedCount - completedCount} />
<ul className="filters">
{[ SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED ].map(filter =>
<li key={filter}>
<FilterLink filter={filter} />
</li>
)}
</ul>
{completedCount > 0 &&
<ClearButton
completedCount={completedCount}
clearCompleted={clearCompleted}
/>
}
</footer>
) : (
<span />
)
)
Footer.propTypes = {
completedCount: PropTypes.number.isRequired,
activeCount: PropTypes.number.isRequired,
filter: PropTypes.string.isRequired,
onClearCompleted: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired
listedCount: PropTypes.number.isRequired,
completedCount: PropTypes.number.isRequired,
}

export default Footer
const mapStateToProps = (state) => ({
filter: state.filter,
listedCount: getListedCount(state),
completedCount: getCompletedCount(state),
})

export default connect(
mapStateToProps,
{ clearCompleted }
)(Footer)
42 changes: 21 additions & 21 deletions components/Header.js
@@ -1,27 +1,27 @@
import React, { PropTypes, Component } from 'react'
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import TodoTextInput from './TodoTextInput'
import { addTodo } from '../actions'

class Header extends Component {
handleSave(text) {
if (text.length !== 0) {
this.props.addTodo(text)
}
}

render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput newTodo
onSave={this.handleSave.bind(this)}
placeholder="What needs to be done?" />
</header>
)
}
}

const Header = ({ addTodo }) => (
<header className="header">
<h1>todos</h1>
<TodoTextInput
newTodo
placeholder="What needs to be done?"
onSave={text => {
if (text.length !== 0) {
addTodo(text)
}
}}
/>
</header>
)
Header.propTypes = {
addTodo: PropTypes.func.isRequired
}

export default Header
export default connect(
null,
{ addTodo }
)(Header)
105 changes: 24 additions & 81 deletions components/MainSection.js
@@ -1,83 +1,26 @@
import React, { Component, PropTypes } from 'react'
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import TodoItem from './TodoItem'
import ToggleAll from './ToggleAll'
import Footer from './Footer'
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'

const TODO_FILTERS = {
[SHOW_ALL]: () => true,
[SHOW_ACTIVE]: todo => !todo.completed,
[SHOW_COMPLETED]: todo => todo.completed
}

class MainSection extends Component {
constructor(props, context) {
super(props, context)
this.state = { filter: SHOW_ALL }
}

handleClearCompleted() {
this.props.actions.clearCompleted()
}

handleShow(filter) {
this.setState({ filter })
}

renderToggleAll(completedCount) {
const { todos, actions } = this.props
if (todos.length > 0) {
return (
<input className="toggle-all"
type="checkbox"
checked={completedCount === todos.length}
onChange={actions.completeAll} />
)
}
}

renderFooter(completedCount) {
const { todos } = this.props
const { filter } = this.state
const activeCount = todos.length - completedCount

if (todos.length) {
return (
<Footer completedCount={completedCount}
activeCount={activeCount}
filter={filter}
onClearCompleted={this.handleClearCompleted.bind(this)}
onShow={this.handleShow.bind(this)} />
)
}
}

render() {
const { todos, actions } = this.props
const { filter } = this.state

const filteredTodos = todos.filter(TODO_FILTERS[filter])
const completedCount = todos.reduce((count, todo) =>
todo.completed ? count + 1 : count,
0
)

return (
<section className="main">
{this.renderToggleAll(completedCount)}
<ul className="todo-list">
{filteredTodos.map(todo =>
<TodoItem key={todo.id} todo={todo} {...actions} />
)}
</ul>
{this.renderFooter(completedCount)}
</section>
)
}
}

MainSection.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}

export default MainSection
import { getVisibleTodoIds } from '../reducers'

const MainSection = ({ visibleIds }) => (
<section className="main">
<ToggleAll />
<ul className="todo-list">
{visibleIds.map(id =>
<TodoItem key={id} id={id} />
)}
</ul>
<Footer />
</section>
)

const mapStateToProps = (state) => ({
visibleIds: getVisibleTodoIds(state)
})

export default connect(
mapStateToProps
)(MainSection)