Реагировать - изменить 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. Каждый раз, когда вы используете компонент класса и вам нужно что-то сделать сразу после обновления состояния, используйте функцию обратного вызова.