空对象崩溃功能

时间:2019-04-11 15:55:49

标签: javascript reactjs

我有一个将选择内容呈现到屏幕上的组件,以便用户可以动态订购数据。

默认状态为:

    state = {
        sortBy: [ { author: 'asc' } ]
    };

上面的第一个对象引用选择的第一行。一个选择更改对象键,下一个更改对象值。

但是当我更改第二行的第一选择时,状态将更改为:

state = {
        sortBy: [ { author: 'asc' }, {} ]
    };

并用TypeError崩溃了我的排序函数:无法读取未定义的属性“ toLowerCase”。

使其生效的唯一方法是首先更改对设置对象值的选择,然后将状态更改为:

state = {
        sortBy: [ { author: 'asc' }, {undefined: 'asc'} ]
    };

然后更改对象键。

将选择内容呈现到屏幕上的组件:

const TYPES = [
    { slug: 'title', description: 'Title' },
    { slug: 'author', description: 'Author' },
    { slug: 'editionYear', description: 'Edition Year' }
];

class BookListSorter extends React.Component {
    state = {
        sortBy: [ { author: 'asc' } ]
    };

    getSortByKeyForIndex = (index) => Object.keys(this.state.sortBy[index] || {})[0];
    getSortByValueForIndex = (index) => Object.values(this.state.sortBy[index] || {})[0];

    changeSort = (key, index) => (e) => {
        const { target } = e;
        this.setState(({ sortBy }) => {
            const type = key === 'type' ? target.value : this.getSortByKeyForIndex(index);
            const direction = key === 'direction' ? target.value : this.getSortByValueForIndex(index);
            return type || direction ? sortBy.splice(index, 1, { [type]: direction }) : sortBy.splice(index, 1);
        });
    };

    filterTypes = (index) => ({ slug }) => {
        const sortByKeys = this.state.sortBy
            .slice(0, index)
            .reduce((keys, sortObj) => keys.concat(Object.keys(sortObj)[0]), []);
        return !sortByKeys.includes(slug);
    };

    render() {
        const { sortBy } = this.state;

        const lastIndex = sortBy.length - 1;
        const shouldAddNewRow = this.getSortByKeyForIndex(lastIndex) && this.getSortByValueForIndex(lastIndex);
        const rowCount = shouldAddNewRow ? sortBy.length + 1 : sortBy.length;

        return (
            <div>
                <h1>Choose sort order</h1>
                {Array.from(Array(Math.min(rowCount, TYPES.length))).map((dummy, index) => (
                    <div>
                        <select
                            defaultValue={this.getSortByKeyForIndex(index)}
                            onChange={this.changeSort('type', index)}
                        >
                            <option value="">None</option>
                            {TYPES.filter(this.filterTypes(index)).map(({ slug, description }) => (
                                <option value={slug}>{description}</option>
                            ))}
                        </select>
                        <select
                            defaultValue={this.getSortByValueForIndex(index)}
                            onChange={this.changeSort('direction', index)}
                        >
                            <option value="asc">None</option>
                            <option value="asc">Ascending</option>
                            <option value="desc">Descending</option>
                        </select>
                        <br />
                    </div>
                ))}
                <br />
                <Books order={sortBy} />
            </div>
        );
    }
}

接收BookListSort状态作为道具并在订购功能上使用的Book组件:

class Books extends Component {
    state = {
        order: this.props.order
    };
    componentWillReceiveProps({ order }) {
        this.setState({ ...this.state, order });
    }
    render() {
        return (
            <div>
                <h2>Books</h2>
                <Query query={BOOKS_QUERY}>
                    {({ data, loading, error }) => {
                        if (loading) return <h4>Loading...</h4>;
                        if (error) return <p>Error</p>;

                        // Filter
                        const cleanedSort = this.state.order.filter(
                            (obj) => Object.entries(obj).length !== 0 && obj.constructor === Object
                        );

                        const orderedBooks = orderBooksBy(data.books, cleanedSort);

                        return orderedBooks.map((book) => <Book key={book.id} {...book} />);
                    }}
                </Query>
            </div>
        );
    }
}

订购功能:

function compareBy(a, b, orderBy) {
    const key = Object.keys(orderBy)[0],
        o = orderBy[key],
        valueA = a[key],
        valueB = b[key];
    if (!(valueA || valueB)) {
        console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
        return 0;
    }
    if (+valueA === +valueA) {
        return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
    } else {
        if (valueA.localeCompare(valueB) > 0) {
            return o.toLowerCase() === 'desc' ? -1 : 1;
        } else if (valueA.localeCompare(valueB) < 0) {
            return o.toLowerCase() === 'desc' ? 1 : -1;
        }
    }
    return 0;
}

function orderBooksBy(books, orderBy) {
    orderBy = Array.isArray(orderBy) ? orderBy : [ orderBy ];
    return books.sort((a, b) => {
        let result;
        for (let i = 0; i < orderBy.length; i++) {
            result = compareBy(a, b, orderBy[i]);
            if (result !== 0) {
                return result;
            }
        }
        return result;
    });
}

export default orderBooksBy;

1 个答案:

答案 0 :(得分:2)

我会用过滤器解决它。 检索sortBy数组时,您可以执行以下操作:

// State
state = {
  sortBy: [ { author: 'asc' }, {} ]
};

// Filter
const cleanedSort = this.state.sortBy.filter(obj => Object.entries(obj).length !== 0 && obj.constructor === Object);

// Get it cleaned
console.log(cleanedSort);