Insert state into strategic components rather than entire app

This commit is contained in:
John Furrow
2015-11-08 21:34:05 -08:00
parent cf11f386d8
commit 535652d074
12 changed files with 227 additions and 126 deletions
@@ -130,10 +130,23 @@ export default class SortDropdown extends React.Component {
onItemSelect(sortBy) {
this.setState({
isExpanded: false,
sortBy
isExpanded: false
});
this.props.onSortChange(sortBy);
let direction = this.props.selectedItem.direction;
if (this.props.selectedItem.property === sortBy.property) {
direction = direction === 'asc' ? 'desc' : 'asc';
} else {
direction = 'asc';
}
let sortProperty = {
displayName: sortBy.displayName,
property: sortBy.property,
direction
};
this.props.onSortChange(sortProperty);
}
render() {
@@ -1,15 +1,17 @@
import { connect } from 'react-redux';
import CSSTransitionGroup from 'react-addons-css-transition-group';
import React from 'react';
import AddTorrents from './AddTorrents';
import { dismissModal } from '../../actions/UIActions';
import Icon from '../icons/Icon';
import uiSelector from '../../selectors/uiSelector';
const methodsToBind = [
'handleOverlayClick'
];
export default class Modal extends React.Component {
class Modal extends React.Component {
constructor() {
super();
@@ -27,10 +29,18 @@ export default class Modal extends React.Component {
this.props.dispatch(dismissModal());
}
shouldComponentUpdate(nextProps) {
if (nextProps.modal !== this.props.modal) {
return true;
} else {
return false;
}
}
render() {
let modal = null;
switch (this.props.type) {
switch (this.props.modal) {
case 'add-torrents':
modal = (
<AddTorrents clickHandler={this.onModalClick}
@@ -60,3 +70,5 @@ export default class Modal extends React.Component {
}
}
export default connect(uiSelector)(Modal);
@@ -1,6 +1,9 @@
import { connect } from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import clientSelector from '../../selectors/clientSelector';
import { fetchTransferData } from '../../actions/ClientActions';
import format from '../../helpers/formatData';
import Icon from '../icons/Icon';
import LineChart from './LineChart';
@@ -8,17 +11,19 @@ import LineChart from './LineChart';
const methodsToBind = [
'componentDidMount',
'componentWillReceiveProps',
'getTransferData',
'shouldComponentUpdate'
];
export default class ClientStats extends React.Component {
class ClientStats extends React.Component {
constructor() {
super();
this.state = {
clientDataFetchInterval: null,
sidebarWidth: 0,
transferData: {
transfers: {
download: [],
upload: []
}
@@ -35,36 +40,50 @@ export default class ClientStats extends React.Component {
});
}
componentWillMount() {
let getTransferData = this.getTransferData;
this.state.clientDataFetchInterval = setInterval(function() {
getTransferData();
}, 5000);
getTransferData();
}
componentWillUnmount() {
clearInterval(this.state.clientDataFetchInterval);
}
componentWillReceiveProps(nextProps) {
// check that the transferData was actually updated since the last component
// check that the transfers was actually updated since the last component
// update. if it was updated, add the latest download & upload rates to the
// end of the array and remove the first element in the array. if the arrays
// are empty, fill in zeros for the first n entries.
if (nextProps.transferData.updatedAt !== this.props.transferData.updatedAt) {
if (nextProps.transfers.updatedAt !== this.props.transfers.updatedAt) {
let index = 0;
let uploadRateHistory = Object.assign([], this.state.transferData.upload);
let downloadRateHistory = Object.assign([], this.state.transferData.download);
let uploadRateHistory = Object.assign([], this.state.transfers.upload);
let downloadRateHistory = Object.assign([], this.state.transfers.download);
if (uploadRateHistory.length === this.props.historyLength) {
uploadRateHistory.shift();
downloadRateHistory.shift();
uploadRateHistory.push(parseInt(nextProps.transferData.upload.rate));
downloadRateHistory.push(parseInt(nextProps.transferData.download.rate));
uploadRateHistory.push(parseInt(nextProps.transfers.upload.rate));
downloadRateHistory.push(parseInt(nextProps.transfers.download.rate));
} else {
while (index < this.props.historyLength) {
if (index < this.props.historyLength - 1) {
uploadRateHistory[index] = 0;
downloadRateHistory[index] = 0;
} else {
uploadRateHistory[index] = parseInt(nextProps.transferData.upload.rate);
downloadRateHistory[index] = parseInt(nextProps.transferData.download.rate);
uploadRateHistory[index] = parseInt(nextProps.transfers.upload.rate);
downloadRateHistory[index] = parseInt(nextProps.transfers.download.rate);
}
index++;
}
}
this.setState({
transferData: {
transfers: {
download: downloadRateHistory,
upload: uploadRateHistory
}
@@ -73,18 +92,22 @@ export default class ClientStats extends React.Component {
}
shouldComponentUpdate(nextProps) {
if (nextProps.transferData.updatedAt !== this.props.transferData.updatedAt) {
if (nextProps.transfers.updatedAt !== this.props.transfers.updatedAt) {
return true;
} else {
return false;
}
}
getTransferData() {
this.props.dispatch(fetchTransferData());
}
render() {
let uploadRate = format.data(this.props.transferData.upload.rate, '/s');
let uploadTotal = format.data(this.props.transferData.upload.total);
let downloadRate = format.data(this.props.transferData.download.rate, '/s');
let downloadTotal = format.data(this.props.transferData.download.total);
let uploadRate = format.data(this.props.transfers.upload.rate, '/s');
let uploadTotal = format.data(this.props.transfers.upload.total);
let downloadRate = format.data(this.props.transfers.download.rate, '/s');
let downloadTotal = format.data(this.props.transfers.download.total);
return (
<div className="client-stats sidebar__item">
@@ -103,7 +126,7 @@ export default class ClientStats extends React.Component {
</div>
</div>
<LineChart
data={this.state.transferData.download}
data={this.state.transfers.download}
height={100}
id="graph--download"
slug="graph--download"
@@ -124,7 +147,7 @@ export default class ClientStats extends React.Component {
</div>
</div>
<LineChart
data={this.state.transferData.upload}
data={this.state.transfers.upload}
height={100}
id="graph--upload"
slug="graph--upload"
@@ -142,3 +165,5 @@ export default class ClientStats extends React.Component {
ClientStats.defaultProps = {
historyLength: 20
};
export default connect(clientSelector)(ClientStats);
@@ -19,6 +19,14 @@ export default class StatusFilters extends React.Component {
});
}
shouldComponentUpdate(nextProps) {
if (nextProps.activeFilter !== this.props.activeFilter) {
return true;
} else {
return false;
}
}
getFilters() {
let filters = [
{
@@ -109,7 +109,6 @@ export default class Torrent extends React.Component {
render() {
let torrent = this.props.data;
let added = new Date(torrent.added * 1000);
let addedString = (added.getMonth() + 1) + '/' + added.getDate() + '/' +
added.getFullYear();
@@ -1,3 +1,4 @@
import { connect } from 'react-redux';
import React from 'react';
import Action from '../components/action-bar/Action';
@@ -5,6 +6,7 @@ import { addTorrent, startTorrent, stopTorrent } from '../actions/ClientActions'
import { displayModal } from '../actions/UIActions';
import { setTorrentsSort } from '../actions/UIActions';
import SortDropdown from '../components/action-bar/SortDropdown';
import uiSelector from '../selectors/uiSelector';
const methodsToBind = [
'handleAddTorrents',
@@ -13,7 +15,7 @@ const methodsToBind = [
'handleStop'
];
export default class FilterBar extends React.Component {
class ActionBar extends React.Component {
constructor() {
super();
@@ -40,11 +42,11 @@ export default class FilterBar extends React.Component {
}
handleStart() {
this.props.dispatch(startTorrent(this.props.uiStore.torrentList.selected));
this.props.dispatch(startTorrent(this.props.torrentList.selected));
}
handleStop() {
this.props.dispatch(stopTorrent(this.props.uiStore.torrentList.selected));
this.props.dispatch(stopTorrent(this.props.torrentList.selected));
}
render() {
@@ -52,7 +54,7 @@ export default class FilterBar extends React.Component {
<nav className="action-bar">
<div className="actions action-bar__item action-bar__item--sort-torrents">
<SortDropdown onSortChange={this.handleSortChange}
selectedItem={this.props.uiStore.torrentList.sortBy} />
selectedItem={this.props.torrentList.sortBy} />
</div>
<div className="actions action-bar__item action-bar__item--torrent-operations">
<div className="action-bar__group">
@@ -73,3 +75,5 @@ export default class FilterBar extends React.Component {
}
}
export default connect(uiSelector)(ActionBar);
+49 -45
View File
@@ -2,7 +2,6 @@ import { connect } from 'react-redux';
import React from 'react';
import ActionBar from '../containers/ActionBar';
import { fetchTorrents, fetchTransferData } from '../actions/ClientActions';
import Modals from '../components/modals/Modals';
import Sidebar from './Sidebar';
import rootSelector from '../selectors/rootSelector';
@@ -10,13 +9,13 @@ import TorrentList from '../containers/TorrentList';
import TorrentListHeader from '../components/torrent-list/TorrentListHeader';
const methodsToBind = [
'componentWillMount',
'componentWillUnmount',
'getTransferData',
'getTorrents'
// 'componentWillMount',
// 'componentWillUnmount',
// 'getTransferData',
// 'getTorrents'
];
class FloodApp extends React.Component {
export default class FloodApp extends React.Component {
constructor() {
super();
@@ -31,54 +30,59 @@ class FloodApp extends React.Component {
this[method] = this[method].bind(this);
});
}
componentWillMount() {
let getTorrents = this.getTorrents;
let getTransferData = this.getTransferData;
this.state.torrentFetchInterval = setInterval(function() {
getTorrents();
}, 5000);
this.state.clientDataFetchInterval = setInterval(function() {
getTransferData();
}, 5000);
this.getTorrents();
this.getTransferData();
}
componentWillUnmount() {
clearInterval(this.state.torrentFetchInterval);
clearInterval(this.state.clientDataFetchInterval);
}
getTransferData() {
this.props.dispatch(fetchTransferData());
}
getTorrents() {
this.props.dispatch(fetchTorrents());
}
//
// componentWillMount() {
// let getTorrents = this.getTorrents;
// let getTransferData = this.getTransferData;
//
// this.state.torrentFetchInterval = setInterval(function() {
// getTorrents();
// }, 5000);
//
// this.state.clientDataFetchInterval = setInterval(function() {
// getTransferData();
// }, 5000);
//
// this.getTorrents();
// this.getTransferData();
// }
//
// componentWillUnmount() {
// clearInterval(this.state.torrentFetchInterval);
// clearInterval(this.state.clientDataFetchInterval);
// }
render() {
return (
<div className="flood">
<Sidebar dispatch={this.props.dispatch}
filterBy={this.props.ui.torrentList.filterBy}
transferData={this.props.client.transfers}/>
<Sidebar />
<main className="content">
<ActionBar dispatch={this.props.dispatch} uiStore={this.props.ui} />
<TorrentList dispatch={this.props.dispatch}
selectedTorrents={this.props.ui.torrentList.selected}
torrents={this.props.torrents}
isFetching={this.props.ui.fetchingData} />
<ActionBar />
<TorrentList />
</main>
<Modals dispatch={this.props.dispatch} type={this.props.ui.modal} />
<Modals />
</div>
);
}
// render() {
// return (
// <div className="flood">
// <Sidebar dispatch={this.props.dispatch}
// filterBy={this.props.ui.torrentList.filterBy}
// transferData={this.props.client.transfers}/>
// <main className="content">
// <ActionBar dispatch={this.props.dispatch} uiStore={this.props.ui} />
// <TorrentList dispatch={this.props.dispatch}
// selectedTorrents={this.props.ui.torrentList.selected}
// torrents={this.props.torrents}
// isFetching={this.props.ui.fetchingData} />
// </main>
// <Modals dispatch={this.props.dispatch} type={this.props.ui.modal} />
// </div>
// );
// }
}
export default connect(rootSelector)(FloodApp);
// export default connect(rootSelector)(FloodApp);
+6 -2
View File
@@ -1,3 +1,4 @@
import { connect } from 'react-redux';
import React from 'react';
import ClientStats from '../components/sidebar/ClientStats';
@@ -5,13 +6,14 @@ import { setTorrentsFilter, setTorrentsSearch } from '../actions/UIActions';
import StatusFilters from '../components/sidebar/StatusFilters';
import SearchBox from '../components/sidebar/SearchBox';
import UIActions from '../actions/UIActions';
import uiSelector from '../selectors/uiSelector';
const methodsToBind = [
'handleFilterChange',
'handleSearchChange'
];
export default class Sidebar extends React.Component {
class Sidebar extends React.Component {
constructor() {
super();
@@ -35,9 +37,11 @@ export default class Sidebar extends React.Component {
<ClientStats transferData={this.props.transferData} />
<SearchBox handleSearchChange={this.handleSearchChange} />
<StatusFilters handleFilterChange={this.handleFilterChange}
activeFilter={this.props.filterBy} />
activeFilter={this.props.torrentList.filterBy} />
</aside>
);
}
}
export default connect(uiSelector)(Sidebar);
@@ -1,9 +1,13 @@
import { connect } from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import { fetchTorrents } from '../actions/ClientActions';
import { handleTorrentClick } from '../actions/UIActions';
import Torrent from '../components/torrent-list/Torrent';
import torrentSelector from '../selectors/torrentSelector';
import UIActions from '../actions/UIActions';
import uiSelector from '../selectors/uiSelector';
const methodsToBind = [
'componentDidMount',
@@ -12,25 +16,28 @@ const methodsToBind = [
'handleTorrentClick',
'handleWindowResize',
'getListPadding',
'getTorrents',
'getViewportLimits',
'setViewportHeight',
'shouldComponentUpdate'
];
export default class TorrentList extends React.Component {
class TorrentList extends React.Component {
constructor() {
super();
this.state = {
count: 0,
maxTorrentIndex: 4,
minTorrentIndex: 0,
scrollPosition: 0,
spaceBottom: 0,
spaceTop: 0,
torrentCount: 0,
torrentFetchInterval: null,
torrentHeight: 64,
torrentRenderBuffer: 2,
minTorrentIndex: 0,
maxTorrentIndex: 4,
spaceTop: 0,
spaceBottom: 0,
scrollPosition: 0,
viewportHeight: 0
};
@@ -46,6 +53,17 @@ export default class TorrentList extends React.Component {
componentWillUnmount() {
window.removeEventListener('resize', this.handleWindowResize);
clearInterval(this.state.torrentFetchInterval);
}
componentWillMount() {
let getTorrents = this.getTorrents;
this.state.torrentFetchInterval = setInterval(function() {
getTorrents();
}, 5000);
getTorrents();
}
shouldComponentUpdate(nextProps) {
@@ -56,6 +74,10 @@ export default class TorrentList extends React.Component {
}
}
getTorrents() {
this.props.dispatch(fetchTorrents());
}
handleTorrentClick(hash, event) {
this.props.dispatch(handleTorrentClick({
hash,
@@ -167,3 +189,5 @@ export default class TorrentList extends React.Component {
}
}
export default connect(torrentSelector)(TorrentList);
@@ -1,10 +1,42 @@
export default function torrentsReducer(state = [], action) {
import { selectTorrents } from '../util/selectTorrents';
const initialState = {
selectedTorrents: [],
torrents: []
};
export default function torrentsReducer(state = initialState, action) {
switch (action.type) {
case 'CLICK_TORRENT':
let event = action.payload.event;
let hash = action.payload.hash;
let selectedTorrents = Object.assign([], state.selectedTorrents);
let torrentList = action.payload.torrentList;
selectedTorrents = selectTorrents({
event,
hash,
selectedTorrents,
torrentList
});
return Object.assign(
{},
state,
{
...state,
selectedTorrents: selectedTorrents
}
);
case 'RECEIVE_TORRENTS':
return Object.assign(
[],
{},
state,
action.payload.torrents
{
...state,
torrents: action.payload.torrents
}
);
default:
+1 -38
View File
@@ -1,5 +1,3 @@
import { selectTorrents } from '../util/selectTorrents';
const initialState = {
fetchingData: true,
modal: null,
@@ -14,45 +12,10 @@ const initialState = {
property: 'added'
}
}
}
};
export default function uiReducer(state = initialState, action) {
switch (action.type) {
case 'CLICK_TORRENT':
let event = action.payload.event;
let hash = action.payload.hash;
let selectedTorrents = Object.assign([], state.torrentList.selected);
let torrentList = action.payload.torrentList;
selectedTorrents = selectTorrents({
event,
hash,
selectedTorrents,
torrentList
});
return Object.assign(
{},
state,
{
...state,
torrentList: {
...state.torrentList,
selected: selectedTorrents
}
}
);
case 'REQUEST_TORRENTS':
return Object.assign(
{},
state,
{
...state,
fetchingData: true
}
);
case 'RECEIVE_TORRENTS':
return Object.assign(
{},
@@ -9,7 +9,9 @@ const torrentListSortBy = state => state.ui.torrentList.sortBy;
const torrentListFilterBy = state => state.ui.torrentList.filterBy;
const torrentList = state => state.torrents;
const selectedTorrents = state => state.torrents.selectedTorrents;
const torrentList = state => state.torrents.torrents;
const filteredTorrents = createSelector(
torrentListFilterBy,
@@ -27,12 +29,23 @@ const searchedTorrents = createSelector(
}
);
const torrentSelector = createSelector(
torrentListSortBy,
const sortedTorrents = createSelector(
searchedTorrents,
(torrentListSortBy, searchedTorrents) => {
torrentListSortBy,
(searchedTorrents, torrentListSortBy) => {
return sortTorrents(searchedTorrents, torrentListSortBy);
}
);
const torrentSelector = createSelector(
selectedTorrents,
sortedTorrents,
(selectedTorrents, sortedTorrents) => {
return {
selectedTorrents,
torrents: sortedTorrents
};
}
);
export default torrentSelector;