在开头反应本机数组映射

时间:2017-10-05 09:16:27

标签: arrays reactjs react-native

我写了一个动画列表组件:

import React, {Component} from 'react';
import PropType from 'prop-types';
import * as Animatable from 'react-native-animatable-promise';
import {FlatList, View} from 'react-native';
import {observable, action, runInAction} from "mobx"
import {observer} from 'mobx-react';
import TransitionGroup, {FadeInOutTransition} from 'react-native-transitiongroup';
import arrayDiff from 'arraydiff';

class AnimatedListItem extends Component {

    static propTypes = {
        animationOut: PropType.string.isRequired,
        animationIn: PropType.string.isRequired,
        duration: PropType.number.isRequired
    };

    tempHeight = 0;
    locked = false;

    componentDidAppear(callback) {
        this.componentWillEnter(callback);
    }

    componentDidEnter(callback) {
        this.refs.Item[this.props.animationIn](this.props.duration).then(() => callback());
    }

    componentWillLeave(callback) {
        this.locked = true;
        this.refs.Item.stopAnimation();
        this.refs.Item.transition({height: this.tempHeight, opacity: 1, scale: 1}, {height: 0, opacity: 0, scale: 0}, this.props.duration, 'ease').then(callback).catch(console.error);
        //this.refs.Item[this.props.animationOut](this.props.duration).then(() => callback());
    }

    _onLayout(event) {
        const {height} = event.nativeEvent.layout;
        if (!this.locked) this.tempHeight = height;
    }

    render() {
        return (<Animatable.View ref='Item' onLayout={this._onLayout.bind(this)}>
            {this.props.children}
        </Animatable.View>);
    }

}

@observer
class AnimatedList extends Component {

    static propTypes = {
        data: PropType.array.isRequired,
        renderItem: PropType.func.isRequired,
        keyExtractor: PropType.func.isRequired,
        inAnimation: PropType.string.isRequired,
        outAnimation: PropType.string.isRequired,
        style: PropType.any,
        duration: PropType.number,
        delay: PropType.number
    };

    @observable keyExtractor = this.props.keyExtractor;
    @observable data = [];
    @observable duration = this.props.duration || 1000;
    @observable outAnimation = this.props.outAnimation;
    @observable inAnimation = this.props.inAnimation;
    _renderItem = this.props.renderItem;
    delay = this.props.delay || 100;

    queue;
    actionIsRunning = false;

    componentDidMount() {
        this.setProps(this.props);
    }

    componentWillReceiveProps(props) {
        this.setProps(props);
    }

    setProps(props) {
        requestAnimationFrame(() => {
            if (this.actionIsRunning) {
                this.queue = props;
                return;
            }
            this.queue = null;
            this.actionIsRunning = true;
            runInAction(() => {
                this.duration = props.duration || 1000;
                this.outAnimation = props.outAnimation;
                this.inAnimation = props.inAnimation;
                this.delay = props.delay;
                this.keyExtractor = props.keyExtractor;
            });
            const differences = arrayDiff(this.data.toJS(), props.data.toJS(), (a, b) => {
                const aKey = this.keyExtractor(a);
                const bKey = this.keyExtractor(b);
                return aKey === bKey;
            });
            console.log("ADIFF: " + JSON.stringify(differences));
            this.applyDifferences(differences).then(() => {
                console.log("NEWLIST: " + JSON.stringify(this.data));
                this.actionIsRunning = false;
                if (this.queue) this.setProps.bind(this)(this.queue);
            }).catch(e => console.error(e));
        });
    }

    async applyDifferences(differences) {
        for (let diff of differences) {
            if (!diff.type) continue;
            await new Promise((resolve) => requestAnimationFrame(resolve));
            if (diff.type === 'remove' && diff.index !== undefined && diff.howMany !== undefined) {
                runInAction(() => {
                    this.data.splice(diff.index, diff.howMany);
                });
            }
            if (diff.type === 'move' && diff.from !== undefined && diff.to !== undefined) {
                runInAction(() => {
                    moveInThisArray(this.data, diff.from, diff.to);
                });
            }
            if (diff.type === 'insert' && diff.index !== undefined && Array.isArray(diff.values)) {
                const argArray = [diff.index, 0];
                for (let value of diff.values) {
                    argArray.push(value);
                }
                runInAction(() => {
                    this.data.splice.apply(this.data, argArray);
                });
            }
            await new Promise((resolve) => setTimeout(resolve, this.delay));
        }
        await new Promise((resolve) => setTimeout(resolve, this.duration));
    }


    /**
     * @typedef {Object} FlatListRenderInfo
     * @property {Object} item
     * @property {Number} index
     * @property {Object} separators
     */

    /**
     *
     * @param {FlatListRenderInfo} info
     */
    renderItem(info) {
        //console.log(this.keyExtractor(info.item));
        return (
            <AnimatedListItem animationOut={this.outAnimation} animationIn={this.inAnimation} duration={this.duration}
                              key={this.keyExtractor(info.item)}>
                {this._renderItem(info)}
            </AnimatedListItem>);
    }

    render() {
        return (<TransitionGroup>
            {this.data.toJS().map((item, index) => {
                return this.renderItem({
                    item: item,
                    index: index,
                    separators: {}
                });
            })}
        </TransitionGroup>)
    }

}

function moveInThisArray(arr, old_index, new_Index) {
    if (new_Index >= arr.length) {
        let k = new_Index - arr.length;
        while ((k--) + 1) {
            arr.push(undefined);
        }
    }
    arr.splice(new_Index, 0, arr.splice(old_index, 1)[0]);
    return arr;
}

export default AnimatedList;

我的大多数代码都按预期工作。但是我有一点问题,如果我在数组的开头添加一个元素,它将在我的列表末尾呈现。我想也许我对Array.map的使用是错误的。数组没有问题,如果我将数组打印到控制台,它的顺序是正确的。我在上一个问题中已经注意到了这个问题,但忽略了它,因为在我的上一个项目中,它对于元素的顺序是重要的,但这一次很重要。是否有替代使用Array.map来解决问题?或者我该怎么办?

1 个答案:

答案 0 :(得分:0)

如果我理解正确,那么你希望你的数组将最后一项映射到第一项,对吧?如果是这样,那么在地图之前使用Array.prototype.reverse()应该可以解决问题。像这样......

render() {
    return (<TransitionGroup>
        {this.data.toJS()
            .reverse()
            .map((item, index) => {
            return this.renderItem({
                item: item,
                index: index,
                separators: {}
            });
        })}
    </TransitionGroup>)
}