Реагировать - изменить this.state onClick с помощью array.map()

Я новичок в React и Javascript, поэтому извините, если это слишком просто.

У меня есть компонент Menu, который отображает анимацию onClick, а затем перенаправляет приложение на другой маршрут, /coffee.

Я хотел бы передать значение, которое было выбрано (выбрано), для функции this.gotoCoffee и обновления this.state.select, но я не знаю как, поскольку я сопоставляю все элементы в this.state.coffees в одном и том же событии onClick.

Как мне это сделать и обновить this.state.select до значения щелчка?

Мой код:

class Menus extends Component{
  constructor (props) {
    super(props);
    this.state = { 
        coffees:[],
        select: '',      
        isLoading: false,
        redirect: false
    };
  };
  gotoCoffee = () => {
    this.setState({isLoading:true})
    setTimeout(()=>{
      this.setState({isLoading:false,redirect:true})
    },5000)
  }

  renderCoffee = () => {
    if (this.state.redirect) {
      return (<Redirect to={'/coffee/${this.state.select}'} />)
    }
  }

  render(){
    const data = this.state.coffees;

    return (
      <div>
        <h1 className="title is-1"><font color="#C86428">Menu</font></h1>
        <hr/><br/>
        {data.map(c => 
          <span key={c}>
            <div>
               {this.state.isLoading && <Brewing />}
               {this.renderCoffee()}
              <div onClick={() => this.gotoCoffee()} 
                  <strong><font color="#C86428">{c}</font></strong></div>
            </div>
          </span>)
        }
      </div>
    );
  }
}

export default withRouter(Menus);

Я попытался передать значение следующим образом:

  gotoCoffee = (e) => {
    this.setState({isLoading:true,select:e})
    setTimeout(()=>{
      this.setState({isLoading:false,redirect:true})
    },5000) 
    console.log(this.state.select)
  }

примерно так:

<div onClick={(c) => this.gotoCoffee(c)} 

или около того:

<div onClick={(event => this.gotoCoffee(event.target.value} 

но console.log(this.state.select) показывает мне "undefined" для обеих попыток.

Похоже, что я передаю Class с 'c'.

браузер показывает мне точно, что на URI при перенаправлении:

http://localhost/coffee/[object%20Object]


Теперь, если я передаю сопоставленное "c" {this.renderCoffee(c)}, которое не является событием onClick, мне удается передать элементы массива.

но мне нужно передать не объект, а нажатое значение 'c' в this.gotoCoffee(c), а затем обновить this.state.select.

Как я могу это исправить?

Ответы

Ответ 1

Вы можете передать index элемента в gotoCoffee с замыканием в render. Затем в gotoCoffee просто получите доступ к этому элементу как this.state.coffees[index].

gotoCoffee = (index) => {
    this.setState({isLoading:true, select: this.state.coffees[index]})
    setTimeout(()=>{
      this.setState({isLoading:false,redirect:true})
    },5000)
  }

  render(){
    const data = this.state.coffees;

    return (
      <div>
        <h1 className="title is-1"><font color="#C86428">Menu</font></h1>
        <hr/><br/>
        {data.map((c, index) => 
          <span key={c}>
            <div>
               {this.state.isLoading && <Brewing />}
               {this.renderCoffee()}
              <div onClick={() => this.gotoCoffee(index)} 
                  <strong><font color="#C86428">{c}</font></strong></div>
            </div>
          </span>)
        }
      </div>
    );
  }
}

Ответ 2

Все ответы выглядят хорошо и работают для вас, и очевидно, что вы допустили ошибку, не указав правильное значение в обработчике кликов. Но так как вы новичок в этой эпохе, я подумал, что лучше изменить вашу реализацию следующим образом:

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

    class Menus extends Component{
        state: {
            /* state properties */
        };
    }
    
  • Когда вы объявляете функции в методе рендеринга, он всегда создает новый, каждый рендеринг, который имеет определенную стоимость и не оптимизирован. Лучше, если вы используете карри:

    handleClick = selected => () => { /* handle click */ }
    render () {
        // ...
        coffees.map( coffee =>
            // ...
             <div onClick={ this.handleClick(coffee) }>
            // ...
    }
    
  • Вы можете перенаправить с помощью history.replace, так как вы обернули свой компонент с withRouter, и это полезно, потому что вы перенаправляете по клику и избавляетесь от метода renderCoffee:

    handleClick = selected => () => 
        this.setState(
            { isLoading: true},
            () => setTimeout(
                () => {
                    const { history } = this.props;
                    this.setState({ isLoading: false });
                    history.replace('/${coffee}');
                }
                , 5000)
        );
    

Поскольку Redirect заменяет маршрут, и я думаю, что вы хотите, чтобы обычная смена страниц не заменяла, я предлагаю вместо этого использовать history.push.

Ответ 3

поэтому, основываясь на вашем коде, вы можете сделать это несколькими способами.

onClick=(event) => this.gotoCoffee(event.target.value)

Это похоже на подход, который вы хотите.

onClick=() => this.gotoCoffee(c)

c будет связан с вашим элементом в массиве.

Ответ 4

Вы фактически почти поняли это в своем вопросе. Бьюсь об заклад, причина, по которой ваше состояние не определено, связана с недолгим характером event. setState является асинхронным действием и не всегда происходит немедленно. Посредством прямой передачи события и разрешения нормальной работы функции событие освобождается до того, как состояние может быть установлено. Мой совет - обновить вашу функцию gotoCoffee так:

 gotoCoffee = (e) => {
    const selectedCoffee = e.target.value
    this.setState({isLoading:true,select:selectedCoffee},() => 
     {console.log(this.state.select})
    setTimeout(()=>{
      this.setState({isLoading:false,redirect:true})
    },5000) 
  }

Обратите внимание, что я переместил вашу строку console.log в функцию обратного вызова в setState, чтобы она не сработала, пока не будет обновлено состояние AFTER. Каждый раз, когда вы используете компонент класса и вам нужно что-то сделать сразу после обновления состояния, используйте функцию обратного вызова.