【问题标题】:React native array map insert at beginning在开始时反应本机数组映射插入
【发布时间】:2017-10-05 09:16:27
【问题描述】:

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

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 来解决问题?或者我该怎么办?

【问题讨论】:

  • 你试过用 unshift 代替 push 吗?
  • 因为它不应该在开始时每次都推送。开头只是一个例子。它也可能在第二个位置等。

标签: arrays reactjs react-native


【解决方案1】:

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

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

【讨论】:

  • 不,嗯,也许我的问题有点令人困惑。当我将 as example 一个项目推到顶部时,它不会在顶部呈现,直到我刷新洞组件。当我将一个项目推到中间或其他地方时也会发生这种情况。它将最新的项目渲染到底部,但它应该以正确的顺序渲染项目而不重新渲染所有内容。
  • 啊!是的,我根本不明白这是问题所在。哈!让我仔细看看。
猜你喜欢
  • 2020-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多