implement react-infinite example
This commit is contained in:
parent
af60c141f5
commit
c866cfd63c
15
spikes/list/react-infinite/.babelrc
Normal file
15
spikes/list/react-infinite/.babelrc
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"sourceMaps": "both",
|
||||
"presets": [
|
||||
"react",
|
||||
"es2015"
|
||||
],
|
||||
"plugins": [
|
||||
"react-hot-loader/babel",
|
||||
"add-module-exports",
|
||||
"syntax-async-functions",
|
||||
["transform-object-rest-spread", {
|
||||
"useBuiltIns": true
|
||||
}]
|
||||
]
|
||||
}
|
34
spikes/list/react-infinite/package.json
Normal file
34
spikes/list/react-infinite/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "react-infinite-spike",
|
||||
"private": true,
|
||||
"license": "private",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --config webpack/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"build-array": "^1.0.0",
|
||||
"delay": "^1.3.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"react": "^15.4.0",
|
||||
"react-dom": "^15.4.0",
|
||||
"react-hot-loader": "^3.0.0-beta.6",
|
||||
"react-infinite": "^0.10.0",
|
||||
"react-redux": "^4.4.6",
|
||||
"redux": "^3.6.0",
|
||||
"redux-logger": "^2.7.4",
|
||||
"redux-promise-middleware": "^4.1.0",
|
||||
"redux-thunk": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.18.2",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.2.7",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.19.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"webpack": "^2.1.0-beta.27",
|
||||
"webpack-dev-server": "^1.16.2"
|
||||
}
|
||||
}
|
6
spikes/list/react-infinite/readme.md
Normal file
6
spikes/list/react-infinite/readme.md
Normal file
@ -0,0 +1,6 @@
|
||||
# react-infinite
|
||||
|
||||
- not fetching when scrolling to the end of a big list
|
||||
- it's keeping the location when we filter (kinda)
|
||||
- tried with 100000 rows and it scrolled flawlessly, but it struggled filtering (I assume that the filter itself it the reason)
|
||||
- because the calculations are based on a fixed container height, we might need to figure out how to handle a responsive ui where the height changes
|
88
spikes/list/react-infinite/src/actions.js
vendored
Normal file
88
spikes/list/react-infinite/src/actions.js
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
const buildArray = require('build-array');
|
||||
const delay = require('delay');
|
||||
|
||||
const actions = {
|
||||
'FETCH_FULFILLED': (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
fetching: false,
|
||||
items: (state.items || []).concat(action.payload)
|
||||
};
|
||||
},
|
||||
'FETCH_PENDING': (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
fetching: true
|
||||
};
|
||||
},
|
||||
'FILTER_PENDING': (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
fetching: true
|
||||
};
|
||||
},
|
||||
'FILTER_FULFILLED': (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
fetching: false,
|
||||
filtered: action.payload.length !== state.items.length
|
||||
? action.payload
|
||||
: null
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const fetch = () => (dispatch, getState) => {
|
||||
const {
|
||||
filtered
|
||||
} = getState();
|
||||
|
||||
if (filtered) {
|
||||
return;
|
||||
}
|
||||
|
||||
return dispatch({
|
||||
type: 'FETCH',
|
||||
payload: delay(500).then(() => {
|
||||
const {
|
||||
items = []
|
||||
} = getState();
|
||||
|
||||
return buildArray(100000).map((v, i) => {
|
||||
const id = items.length + i;
|
||||
|
||||
return {
|
||||
id,
|
||||
title: `test ${id}`
|
||||
};
|
||||
});
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
const filter = (payload) => (dispatch, getState) => {
|
||||
const regexp = new RegExp(payload);
|
||||
|
||||
return dispatch({
|
||||
type: 'FILTER',
|
||||
payload: delay(500).then(() => {
|
||||
const {
|
||||
items = []
|
||||
} = getState();
|
||||
|
||||
return items.filter((item) => {
|
||||
return regexp.test(item.title);
|
||||
}).sort((a, b) => a.id - b.id);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = (state, action) => {
|
||||
return actions[action.type]
|
||||
? actions[action.type](state, action)
|
||||
: state;
|
||||
};
|
||||
|
||||
module.exports.fetch = fetch;
|
||||
module.exports.filter = filter;
|
||||
|
18
spikes/list/react-infinite/src/index.js
vendored
Normal file
18
spikes/list/react-infinite/src/index.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
const Store = require('./store');
|
||||
const ReactDOM = require('react-dom');
|
||||
const React = require('react');
|
||||
|
||||
const render = () => {
|
||||
const Root = require('./root');
|
||||
|
||||
ReactDOM.render(
|
||||
<Root store={Store()} />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
};
|
||||
|
||||
render();
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./root', render);
|
||||
}
|
76
spikes/list/react-infinite/src/list.js
vendored
Normal file
76
spikes/list/react-infinite/src/list.js
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
const debounce = require('lodash.debounce');
|
||||
const ReactRedux = require('react-redux');
|
||||
const Infinite = require('react-infinite');
|
||||
const actions = require('./actions');
|
||||
const React = require('react');
|
||||
|
||||
const {
|
||||
connect
|
||||
} = ReactRedux;
|
||||
|
||||
const {
|
||||
fetch,
|
||||
filter
|
||||
} = actions;
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
return {
|
||||
fetch: () => {
|
||||
return dispatch(fetch());
|
||||
},
|
||||
filter: (payload) => {
|
||||
return dispatch(filter(payload));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const List = ({
|
||||
items = [],
|
||||
filtered,
|
||||
input = '',
|
||||
fetching = false,
|
||||
fetch,
|
||||
filter
|
||||
}) => {
|
||||
const _items = (filtered || items).map((item) => {
|
||||
return (
|
||||
<div key={item.id}>
|
||||
{item.title}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const _loading = (
|
||||
<div>
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
|
||||
const _filter = debounce(filter, 100);
|
||||
const onChange = (ev) => _filter(ev.target.value);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input onChange={onChange} />
|
||||
<Infinite
|
||||
containerHeight={200}
|
||||
elementHeight={20}
|
||||
infiniteLoadBeginEdgeOffset={200}
|
||||
onInfiniteLoad={fetch}
|
||||
isInfiniteLoading={fetching}
|
||||
loadingSpinnerDelegate={_loading}
|
||||
>
|
||||
{_items}
|
||||
</Infinite>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(List);
|
24
spikes/list/react-infinite/src/root.js
vendored
Normal file
24
spikes/list/react-infinite/src/root.js
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
const ReactRedux = require('react-redux');
|
||||
const ReactHotLoader = require('react-hot-loader');
|
||||
const React = require('react');
|
||||
const List = require('./list');
|
||||
|
||||
const {
|
||||
AppContainer
|
||||
} = ReactHotLoader;
|
||||
|
||||
const {
|
||||
Provider
|
||||
} = ReactRedux;
|
||||
|
||||
module.exports = ({
|
||||
store
|
||||
}) => {
|
||||
return (
|
||||
<AppContainer>
|
||||
<Provider store={store}>
|
||||
<List />
|
||||
</Provider>
|
||||
</AppContainer>
|
||||
);
|
||||
};
|
19
spikes/list/react-infinite/src/store.js
vendored
Normal file
19
spikes/list/react-infinite/src/store.js
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
const createLogger = require('redux-logger');
|
||||
const promiseMiddleware = require('redux-promise-middleware').default;
|
||||
const thunk = require('redux-thunk').default;
|
||||
const redux = require('redux');
|
||||
const reducer = require('./actions');
|
||||
|
||||
const {
|
||||
createStore,
|
||||
compose,
|
||||
applyMiddleware
|
||||
} = redux;
|
||||
|
||||
module.exports = (state = Object.freeze({})) => {
|
||||
return createStore(reducer, state, applyMiddleware(
|
||||
createLogger(),
|
||||
promiseMiddleware(),
|
||||
thunk
|
||||
));
|
||||
};
|
7
spikes/list/react-infinite/static/.gitignore
vendored
Normal file
7
spikes/list/react-infinite/static/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
*
|
||||
!.gitignore
|
||||
!.gitkeep
|
||||
!index.html
|
||||
|
||||
js/*
|
||||
!js/.gitkeep
|
10
spikes/list/react-infinite/static/index.html
Normal file
10
spikes/list/react-infinite/static/index.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<html lang='en-US'>
|
||||
<head>
|
||||
<title>Infinite List</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id='root'></div>
|
||||
<script src='main.js'></script>
|
||||
</body>
|
||||
</html>
|
30
spikes/list/react-infinite/webpack/base.js
vendored
Normal file
30
spikes/list/react-infinite/webpack/base.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const plugins = {
|
||||
'no-errors-plugin': new webpack.NoErrorsPlugin(),
|
||||
};
|
||||
|
||||
exports.config = {
|
||||
context: path.join(__dirname, '../'),
|
||||
output: {
|
||||
path: path.join(__dirname, '../static'),
|
||||
publicPath: '/',
|
||||
filename: '[name].js'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NoErrorsPlugin()
|
||||
],
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /js?$/,
|
||||
exclude: /node_modules/,
|
||||
include: [
|
||||
path.join(__dirname, '../src')
|
||||
],
|
||||
loader: 'babel-loader'
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
exports.plugins = plugins;
|
29
spikes/list/react-infinite/webpack/index.js
vendored
Normal file
29
spikes/list/react-infinite/webpack/index.js
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
const base = require('./base.js');
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const devServer = {
|
||||
contentBase: [
|
||||
path.join(__dirname, '../static/')
|
||||
],
|
||||
hot: true,
|
||||
compress: true,
|
||||
lazy: false,
|
||||
historyApiFallback: {
|
||||
index: './index.html'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Object.assign(base.config, {
|
||||
entry: [
|
||||
'react-hot-loader/patch',
|
||||
'webpack-dev-server/client?http://localhost:8080',
|
||||
'webpack/hot/only-dev-server',
|
||||
'./src/index.js'
|
||||
],
|
||||
plugins: base.config.plugins.concat([
|
||||
new webpack.HotModuleReplacementPlugin()
|
||||
]),
|
||||
devtool: 'source-map',
|
||||
devServer
|
||||
});
|
Loading…
Reference in New Issue
Block a user