Как фильтровать данные, используя флажки, без использования приставки - PullRequest
0 голосов
/ 03 января 2019

Я использую React для отображения названий книг, которые я хочу отфильтровать по категории. Я хочу, чтобы заголовки были отфильтрованы после того, как установлен флажок рядом с названием категории. Я не использую кнопку отправки.

Я немного новичок в React и читаю документацию о "поднятии состояния", но я не смог заставить это работать. Я еще не читал документацию по Hooks или Context API. Возможно, это решение, но кажется, что то, что я делаю, не достаточно сложно для этого ... а может и нет?

class Checkbox extends Component {
    state = {
        checked: false
    }

    handleClick = (e) => {
        this.setState(() => ({ checked: !this.state.checked }))
    }

    render() {
        const name = this.props.name;
        return (
            <label className="form__group">
                <input type="checkbox" checked={this.state.checked} onChange={this.handleClick} className="form__input" />
                <span className="form__faux-input"></span>
                <span className="form__label">{name}</span>
            </label>
        )
    }
}

function Sidebar({ categories }) {
    return (
        <div className="sidebar">
            <div className="controls">
                <div className="filter">
                    <h2 className="filter__heading">Filter By Category</h2>
                    <form className="filter-form">
                        {!categories
                            ? <Spinner />
                            : categories.map((item) => (
                                <Checkbox key={item} name={item} />
                            ))
                        }
                        <div className="form__group">
                            <button className="btn btn--rectangle btn--green">
                                <span className="btn-wrapper">Reset</span>
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    );
}

class App extends Component {
    state = {
        books: null,
        categories: null
    }

    async componentDidMount() {
        const { books, categories } = await getBooks();

        this.setState(() => ({
            books: books,
            categories: categories
        }));
    }

    render() {
        const { books } = this.state;
        const { categories } = this.state;
        return (
            <div className="App">
                <Header />
                <main className="main">
                    <div className="uiContainer">
                        <Sidebar
                            categories={categories}
                        />
                        {!books
                            ? <Spinner />
                            : <Card books={books} />
                        }
                    </div>
                </main>
            </div>
        );
    }
}

Ответы [ 2 ]

0 голосов
/ 03 января 2019

Родительский компонент App должен иметь возможность сообщить Card, что такое выбранная категория, при условии, что Card - это то место, где отображается список.

Для этого вы можете:

1) создать функцию обратного вызова внутри <App>:

_setCurrentCategory(selection) { 
  this.setState({currentCategory: selection}) 
}

2) передать его <Checkbox /> в качестве реквизита и использовать в onChange:

  class Checkbox extends Component {
    render() {
        const {name, setCurrentCategoryCallback } = this.props
        return (
            <label className="form__group">
                <input
                  type="checkbox"
                  onChange={() => setCurrentCategoryCallback(name)}
                  className="form__input"
                />
                <span className="form__faux-input"></span>
                <span className="form__label">{name}</span>
            </label>
        )
    }
  }

.. это изменит состояние в родительском элементе, чтобы вы могли

2) затем передайте состояние от <App /> до <Card />:

<Card 
  currentCategory={this.state.currentCategory} 
  books={books}
/> 

^^ при условии, что именно здесь будет отображаться отфильтрованный список. Внутри компонента Card вы можете отфильтровать / упорядочить, а затем отобразить список по своему усмотрению, поскольку теперь в нем есть как список книг, так и выбранная в данный момент категория.

Это очень слабо закодировано, но, надеюсь, вы поняли идею!

также, при деконструкции вам не нужно делать это:

const { books } = this.state;
const { categories } = this.state; 

вместо этого вы можете сделать это: const { books, categories} = this.state, так как они оба из штата :)

0 голосов
/ 03 января 2019

Я не на 100% понимаю вопрос, но если вы хотите создать такой раздел, как

[x]  cats
[x]  dogs
[ ]  rabbits // dont show rabbits

, тогда вы можете сохранить выделение и часть результата в одном элементе реакции, если вы не понимаете 'поднятие состояния 'секция

состояние должно содержать массив объектов, подобных этому:

[{
    allow: true,
    title: 'cat'
},
{allow: false, title: 'rabbit'}]

Для обновления списка используйте что-то вроде этого:

this.state.map(({title, allow}) => (
    <div>
        <TickBox onClick={() => this.toggleAnimal(title)} value={allow}/>
        <p>{animalName}</p>
    </div>
)

toggleAnimalФункция должна найти животное, используя заголовок, и обновить состояние

Затем вы можете отфильтровать всех недопустимых животных

this.state
   .filter(animal => animal.allowed)
   .map(a => <p>{a.title}</p>)

поднимая состояние вверх

На данный момент у вас есть 1 компонент, и функция рендеринга выглядит следующим образом:

<h1>Please select the animals</h1>
{
    animals.map(_ => <div><tickbox onClick={() => this.handleToggle(title)} /><title></div>)
}
<h1>Here are the filtered animals</h1>
{
    animals.filter(a => a.allow).map(animal => animal.title).map(/* to JSX */)
}

Было бы красивее и отзывчивее, если бы корневой компонент выглядел так:

render () {
    <SelectAnimals toggle={handleToggle} animals={this.state} />
    <ShowFilteredAnimals animals={this.state} />
}
handleToggle (title) {
    this.setState(...)
}

Как можно видеть, SelectAnimals получает функцию в качестве аргумента, она может связываться со своим родителем, вызывая props.toggle (с заголовком в качестве аргумента)

Так что SelectAnimals будет выглядеть так:

props.animals.map(animal => (
 <div>
   <TickBox onClick=(() => {props.toggle(animal.title)}) /> // HERE
   <p>{animal.title}</p>
 </div>
))

Таким образом, когда флажок запускает событие щелчка, он вызывает функцию стрелки.который вызывает функцию props.toggle с заголовком. В родительском элементе SelectAnimals родительский элемент привязывает функцию-обработчик к SelectAnimals.toggle следующим образом:

handleToggle (title) { // the child element called this function, it just got copied
}

PS: я сделал несколько переименований в своем коде функцией handleToggleможет быть таким же, как toggleAnimals

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...