Skip to content

Commit

Permalink
Refactor BufferList{,Item}
Browse files Browse the repository at this point in the history
* Replace connected BufferList with useSelector
* Use createSelector to cache sorted buffers
* Use createSelector to cache empty hotlist
* Make BufferListItem a PureComponent to prevent unnecessary rerenders
* Move buffer list item visibility into BufferListItem, remove filter
  • Loading branch information
mhoran committed Apr 24, 2024
1 parent 035e5ce commit 350d197
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 71 deletions.
27 changes: 17 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"react-redux": "^8.0.2",
"redux": "^4.0.5",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.4.2"
"redux-thunk": "^2.4.2",
"reselect": "^5.1.0"
},
"devDependencies": {
"@babel/core": "^7.19.3",
Expand Down
2 changes: 1 addition & 1 deletion src/store/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HotListState } from './hotlists';

const createEmptyHotlist = (bufferId: string) => ({
export const createEmptyHotlist = (bufferId: string) => ({
buffer: bufferId,
count: [0, 0, 0, 0],
message: 0,
Expand Down
5 changes: 1 addition & 4 deletions src/usecase/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const connector = connect((state: StoreState) => {
);

return {
buffers: state.buffers,
currentBufferId,
currentBuffer,
hasHighlights: numHighlights > 0,
Expand Down Expand Up @@ -177,14 +176,12 @@ class App extends React.Component<Props, State> {
}

render() {
const { buffers, currentBufferId, currentBuffer, hasHighlights } =
this.props;
const { currentBufferId, currentBuffer, hasHighlights } = this.props;

const { showTopic, drawerWidth, showNicklistModal } = this.state;

const sidebar = () => (
<BufferList
buffers={Object.values(buffers).sort((a, b) => a.number - b.number)}
currentBufferId={currentBufferId}
onSelectBuffer={this.changeCurrentBuffer}
/>
Expand Down
96 changes: 48 additions & 48 deletions src/usecase/buffers/ui/BufferList.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,68 @@
import { createSelector } from 'reselect';
import * as React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, FlatList, View, ListRenderItem } from 'react-native';
import BufferListItem from './BufferListItem';
import { useCallback } from 'react';
import { FlatList, ListRenderItem, StyleSheet, View } from 'react-native';
import { StoreState } from '../../../store';
import { getHotlistForBufferId } from '../../../store/selectors';
import { useAppSelector } from '../../../store/hooks';
import { createEmptyHotlist } from '../../../store/selectors';
import BufferListItem from './BufferListItem';
import { HotListState } from '../../../store/hotlists';

interface Props {
buffers: WeechatBuffer[];
currentBufferId: string | null;
onSelectBuffer: (b: WeechatBuffer) => void;
hotlists: HotListState;
filterBuffers: boolean;
}

const keyExtractor = (buffer: WeechatBuffer): string => buffer.id;

class BufferList extends React.Component<Props> {
renderListItem: ListRenderItem<WeechatBuffer> = ({ item }) => {
const { onSelectBuffer, currentBufferId, hotlists } = this.props;
const selectBuffers = createSelector(
[(state: StoreState) => state.buffers],
(buffers) => Object.values(buffers).sort((a, b) => a.number - b.number)
);

return (
<BufferListItem
buffer={item}
onSelectBuffer={onSelectBuffer}
currentBufferId={currentBufferId}
hotlist={getHotlistForBufferId(hotlists, item.id)}
/>
);
};
const selectHotlist = createSelector(
[
(hotlist: HotListState, bufferId: string) => hotlist[bufferId],
(hotlist: HotListState, bufferId: string) => bufferId
],
(hotlist, bufferId) => hotlist || createEmptyHotlist(bufferId)
);

visibleBuffer = (buffer: WeechatBuffer) => {
const { filterBuffers, hotlists } = this.props;
if (filterBuffers) {
const BufferList: React.FC<Props> = ({ currentBufferId, onSelectBuffer }) => {
const buffers = useAppSelector(selectBuffers);
const hotlists = useAppSelector((state) => state.hotlists);
const filterBuffers = useAppSelector(
(state) => state.connection.filterBuffers
);

const renderListItem: ListRenderItem<WeechatBuffer> = useCallback(
({ item }) => {
return (
(buffer.local_variables.type &&
buffer.local_variables.type !== 'server') ||
(hotlists[buffer.id] && hotlists[buffer.id].sum !== 0)
<BufferListItem
buffer={item}
onSelectBuffer={onSelectBuffer}
isCurrentBuffer={currentBufferId === item.id}
hotlist={selectHotlist(hotlists, item.id)}
filterBuffers={filterBuffers}
/>
);
} else {
return true;
}
};
},
[currentBufferId, hotlists, onSelectBuffer, filterBuffers]
);

render() {
const { buffers } = this.props;

return (
<View style={styles.container}>
<FlatList
style={styles.container}
data={buffers.filter(this.visibleBuffer)}
keyExtractor={keyExtractor}
renderItem={this.renderListItem}
/>
</View>
);
}
}
return (
<View style={styles.container}>
<FlatList
style={styles.container}
data={buffers}
keyExtractor={keyExtractor}
renderItem={renderListItem}
/>
</View>
);
};

export default connect((state: StoreState) => ({
hotlists: state.hotlists,
filterBuffers: state.connection.filterBuffers
}))(BufferList);
export default BufferList;

const styles = StyleSheet.create({
container: {
Expand Down
25 changes: 18 additions & 7 deletions src/usecase/buffers/ui/BufferListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import {
interface Props {
buffer: WeechatBuffer;
hotlist: Hotlist;
currentBufferId: string | null;
isCurrentBuffer: boolean;
onSelectBuffer: (b: WeechatBuffer) => void;
filterBuffers: boolean;
}

export default class BufferListItem extends React.Component<Props> {
export default class BufferListItem extends React.PureComponent<Props> {
getBufferViewStyleFromProps = (): TextStyle | null => {
const { currentBufferId, buffer, hotlist } = this.props;
const { isCurrentBuffer, hotlist } = this.props;

if (currentBufferId === buffer.id) {
if (isCurrentBuffer) {
return { backgroundColor: '#f2777a' };
} else if (hotlist.highlight > 0) {
return { backgroundColor: '#ffcf7f' };
Expand All @@ -30,9 +31,9 @@ export default class BufferListItem extends React.Component<Props> {
};

getBufferTextStyleFromProps = (): TextStyle | null => {
const { currentBufferId, buffer, hotlist } = this.props;
const { isCurrentBuffer, hotlist } = this.props;

if (currentBufferId === buffer.id) {
if (isCurrentBuffer) {
return { color: '#fff' };
} else if (hotlist.highlight > 0) {
return { color: '#000' };
Expand All @@ -44,7 +45,17 @@ export default class BufferListItem extends React.Component<Props> {
};

render() {
const { buffer, hotlist, onSelectBuffer } = this.props;
const { buffer, hotlist, onSelectBuffer, filterBuffers, isCurrentBuffer } =
this.props;

if (
filterBuffers &&
!isCurrentBuffer &&
(!buffer.local_variables.type ||
buffer.local_variables.type === 'server') &&
hotlist.sum === 0
)
return;

return (
<TouchableHighlight
Expand Down

0 comments on commit 350d197

Please sign in to comment.