UNPKG

react-windowed-list

Version:

A fast, versatile virtual-render list component for React

177 lines (152 loc) 5.78 kB
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // external dependencies import raf from 'raf'; import { findDOMNode } from 'react-dom'; // constants import { MAX_SYNC_UPDATES, ADD_EVENT_LISTENER_OPTIONS, UNSTABLE_MESSAGE, UNSTABLE_TIMEOUT } from './constants'; // utils import { getFromAndSize, noop } from './utils'; /** * @function onConstruct * * @description * on construction, set the initial state * * @param {ReactComponent} instance the component instance */ export var onConstruct = function onConstruct(instance) { var props = instance.props, setReconcileFrameAfterUpdate = instance.setReconcileFrameAfterUpdate, state = instance.state; var itemsPerRow = 1; setReconcileFrameAfterUpdate(); instance.state = _extends({}, state, getFromAndSize(props.initialIndex, 0, itemsPerRow, props), { itemsPerRow: itemsPerRow }); }; /** * @function componentDidMount * * @description * on mount, update the frame with the desired scroll position * * @param {ReactComponent} instance the component instance */ export var componentDidMount = function componentDidMount(instance) { instance.outerContainer = findDOMNode(instance); if (!instance.props.isHidden) { raf(function () { return instance.updateFrame(instance.scrollTo); }); } }; /** * @function getDerivedStateFromProps * * @description * get the next state based on the next props * * @param {Object} nextProps the next props passed * @param {number} from the first index to render * @param {number} itemsPerRow the number of items per row * @param {number} size the number of items to render * @returns {Object|null} the next state, if applicable */ export var getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, _ref) { var from = _ref.from, itemsPerRow = _ref.itemsPerRow, size = _ref.size; var fromAndSize = getFromAndSize(from, size, itemsPerRow, nextProps); return fromAndSize.from === from && fromAndSize.size === size ? null : fromAndSize; }; /** * @function componentWillReceiveProps * * @description * when props are received, set the state if the next calculated state is different * * @param {number} debounceReconciler the debounce reconciler in props * @param {function} setReconcileFrameAfterUpdate the method to set the reconciler frame * @param {function} setStateIfAppropriate the method to set the state if changed * @param {number} from the first index to render * @param {number} itemsPerRow the number of items per row * @param {number} size the number of items to render * @param {Object} nextProps the incoming props */ export var componentWillReceiveProps = function componentWillReceiveProps(_ref2, _ref3) { var debounceReconciler = _ref2.props.debounceReconciler, setReconcileFrameAfterUpdate = _ref2.setReconcileFrameAfterUpdate, setStateIfAppropriate = _ref2.setStateIfAppropriate, _ref2$state = _ref2.state, from = _ref2$state.from, itemsPerRow = _ref2$state.itemsPerRow, size = _ref2$state.size; var nextProps = _ref3[0]; if (debounceReconciler !== nextProps.debounceReconciler) { setReconcileFrameAfterUpdate(); } setStateIfAppropriate(getFromAndSize(from, size, itemsPerRow, nextProps), noop); }; /** * @function getSnapshotBeforeUpdate * * @description * update the debounce reconciler if the values have changed in props * * @param {number} debounceReconciler the delay to wait before reconciling in props * @param {function} setReconcileFrameAfterUpdate the method to set the reconciler frame * @param {Object} previousProps the previous props values * @returns {void} */ export var getSnapshotBeforeUpdate = function getSnapshotBeforeUpdate(_ref4, _ref5) { var debounceReconciler = _ref4.props.debounceReconciler, setReconcileFrameAfterUpdate = _ref4.setReconcileFrameAfterUpdate; var previousProps = _ref5[0]; return debounceReconciler !== previousProps.debounceReconciler && setReconcileFrameAfterUpdate(); }; /** * @function componentDidUpdate * * @description * update the frame position, cutting off after a certain point if unstable * * @param {ReactComponent} instance the component instance * @returns {void} */ export var componentDidUpdate = function componentDidUpdate(instance) { if (instance.unstableTimeoutId || ++instance.updateCounter > MAX_SYNC_UPDATES) { clearTimeout(instance.unstableTimeoutId); if (!instance.unstableTimeoutId) { console.error(UNSTABLE_MESSAGE); // eslint-disable-line no-console } instance.unstableTimeoutId = setTimeout(function () { instance.unstableTimeoutId = null; instance.updateCounter = 0; }, UNSTABLE_TIMEOUT); return; } if (!instance.updateCounterTimeoutId) { instance.updateCounterTimeoutId = raf(function () { instance.updateCounter = 0; instance.updateCounterTimeoutId = null; }); } if (!instance.props.isHidden) { instance.reconcileFrameAfterUpdate(instance.updateFrame); } }; /** * @function componentWillUnmount * * @description * before unmount, remove any listeners applied to the scroll container * * @param {ReactComponent} instance the component instance */ export var componentWillUnmount = function componentWillUnmount(instance) { if (instance.scrollParent) { instance.scrollParent.removeEventListener('scroll', instance.updateFrame, ADD_EVENT_LISTENER_OPTIONS); instance.scrollParent.removeEventListener('mousewheel', noop, ADD_EVENT_LISTENER_OPTIONS); } instance.outerContainer = null; };