简体   繁体   English

无法通过组件中的操作更改Redux状态

[英]Cannot change redux state with a action in a component

Action on button does not perform the action to change the sorting object which sort all itens in a list (another component). 按钮上的操作不执行更改排序对象的操作,该对象对列表中的所有项目进行排序(另一个组件)。 I expect the button to perform this changes passing the sortBy variable on this.props.dispatch(orderBy(sortBy)) or another dynamic way without a button. 我希望按钮通过this.props.dispatch(orderBy(sortBy))上的sortBy变量执行此更改,或者通过另一种没有按钮的动态方式进行更改。

import React from 'react';
import { connect } from 'react-redux';
import orderBy from '../actions/sorting';

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

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

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

      changeSort = (key, index) => e => {
        // This is what is called when an select option changes
        const { target } = e; // Save target from event to use in the callback
        this.setState(({ sortBy }) => {
          // Use a function for atomicness - this prevents state from being out of sync

          // Get the type from the event object if the onchange handler is for the type,
          //  otherwise get from sortBy object
          const type =
            key === "type" ? target.value : this.getSortByKeyForIndex(index);
          // Get the direction from the event object if the onchange handler is for the direction,
          //  otherwise get from sortBy object
          const direction =
            key === "direction" ? target.value : this.getSortByValueForIndex(index);
          // If both options are set, replace the indexed spot in the sortby object
          // Return updated state.
          return type || direction
            ? sortBy.splice(index, 1, { [type]: direction })
            : sortBy.splice(index, 1);
        });
      };

      filterTypes = index => ({ slug }) => {
        // Filter out already used keys from previous rows
        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;
        // Only add a new row if the one above it is completely filled out
        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>
                  <span>Row {index}: </span>
                  <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="">None</option>
                    <option value="asc">Ascending</option>
                    <option value="desc">Descending</option>
                  </select>
                  <br />
                </div>
              )
            )}
            <br />
            <button onClick={() => this.props.dispatch(orderBy(sortBy))}>sort</button>
          </div>
        );
      }
};

const mapStateToProps = (state) => {
    return {
        sorting: state.sorting
    };
};

//ACTIONS

//ADD BOOK

const addBook = ({ title = '', author = '', editionYear = 0} = {}) => ({
    type: 'ADD_BOOK',
    book: {
        title,
        author,
        editionYear
    }
});

//SORT BY

const orderBy = (order) => ({
    type: 'SORT_BY',
    orderBy: order
});

//book reducer

const bookReducerDefaultState = [];
const bookReducer = (state = bookReducerDefaultState, action) => {
    switch(action.type) {
        case 'ADD_BOOK': 
            return [
                ...state,
                action.book
            ];
        default:    
            return state;
    };
};

//sorting reducer

const sortingReducerDefaultState = { 
   orderBy: [{title: 'asc'},{author: 'asc'}]
};
const sortingReducer = (state = sortingReducerDefaultState, action) => {
    switch(action.type) {
        case 'SORT_BY':
            return {
                ...state,
                orderBy: action.orderBy
            };
        default: 
            return state;
    };   
}

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 getSortedBooks(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
    })
  }


//store creation 

const store = createStore(
    combineReducers({
        books: bookReducer,
        sorting: sortingReducer
    })
);

store.subscribe(() => {
    const state = store.getState();    
    const sortedBooks = getSortedBooks(state.books, state.sorting.orderBy) 
    console.log(sortedBooks);
});

export default connect(mapStateToProps)(BookListSorter);

Can anyone help with this issue. 任何人都可以帮助解决这个问题。 Since the button i set up is not working? 由于我设置的按钮不起作用?

Note: This was an answer to the original question 注意:这是原始问题的答案


The best way to get the value of a select element in React is to add an onChange handler. 在React中获取select元素值的最好方法是添加一个onChange处理程序。

In your example, it might look something like this: 在您的示例中,它可能看起来像这样:

<select onChange={(event) => this.setState({ firstType: event.target.value })}>
  <option value="title">Title</option>
  <option value="author">Author</option>
  <option value="editionYear">Edition Year</option>
</select>
<select onChange={(event) => this.setState({ firstDirection: event.target.value })}>
  <option value="asc">Ascending</option>
  <option value="desc">Descending</option>
</select>

By changing the above select inputs, the state would look like this: 通过更改上述选择输入,状态将如下所示:

{
  firstType: 'author',
  firstDirection: 'desc'
}

(The state wont automatically be set until changes are made, so you would have to initialize separately.) (在进行更改之前,不会自动设置状态,因此您必须单独进行初始化。)

You would then need to transform that object into the shape you need. 然后,您需要将该对象转换为所需的形状。

This is just an example, I'll leave it up to you to transform the state into the shape that you need and to connect up redux since it looks like that's what you intend to do with the import of connect . 这只是一个例子,我将其交给您,将状态转换为所需的形状并连接redux,因为这看起来就像您打算对connect进行导入一样。

Note: if the option tags don't have a value attribute set, the value in event.target.value would be the content inside the tags. 注意:如果option标签未设置value属性,则event.target.value的值将是标签内的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM