javascript - Reaccionar: cambie this.state onClick renderizado con array.map()
reactjs (4)
Soy nuevo en React y Javascript, lo siento si esto es demasiado básico.
Tengo un componente de menú que muestra una animación en
onClick
y luego redirige la aplicación a otra ruta,
/coffee
.
Me gustaría pasar el valor en el que se hizo clic (seleccionado) para funcionar
this.gotoCoffee
y actualizar
this.state.select
, pero no sé cómo, ya que estoy asignando todos los elementos en
this.state.coffees
en el mismo
onClick
evento.
¿Cómo hago esto y actualizo
this.state.select
al valor seleccionado?
Mi código:
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);
He intentado pasar el valor así:
gotoCoffee = (e) => {
this.setState({isLoading:true,select:e})
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000)
console.log(this.state.select)
}
un me gusta así:
<div onClick={(c) => this.gotoCoffee(c)}
más o menos:
<div onClick={(event => this.gotoCoffee(event.target.value}
pero
console.log(this.state.select)
me muestra ''
undefined
'' para ambos intentos.
Parece que estoy pasando la
Class
con ''c''.
el navegador me muestra precisamente eso en la uri en la redirección:
http://localhost/coffee/[object%20Object]
Ahora, si paso ''c'' mapeado a
{this.renderCoffee(c)}
, que no es un evento
onClick
, me las arreglo para pasar los elementos de la matriz.
pero necesito pasar no el objeto, sino el valor ''c'' seleccionado a
this.gotoCoffee(c)
, y LUEGO actualizar
this.state.select
.
¿Cómo puedo solucionar esto?
De hecho, casi lo tienes en tu pregunta.
Apuesto a que la razón por la cual su estado no está definido se debe a la naturaleza efímera del
event
.
setState
es una acción asincrónica y no siempre ocurre inmediatamente.
Al pasar el evento directamente y permitir que la función proceda normalmente, el evento se libera antes de que se pueda establecer el estado.
Mi consejo sería actualizar su función
gotoCoffee
para esto:
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)
}
Tenga en cuenta que moví su línea console.log a una función de devolución de llamada dentro de setState para que no se active hasta que el estado DESPUÉS se haya actualizado. Cada vez que use un componente de clase y necesite hacer algo inmediatamente después de actualizar el estado, use la función de devolución de llamada.
Puede pasar el
index
del elemento a
gotoCoffee
con cierre en
render
.
Luego, en
gotoCoffee
, solo acceda a ese elemento como
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>
);
}
}
Todas las respuestas se ven bien y funcionan para usted y es obvio que cometió un error al no pasar el valor correcto en el controlador de clics. Pero como eres nuevo en esta era, pensé que era mejor cambiar tu implementación de esta manera:
-
No es necesario usar el constructor en absoluto y puede declarar una propiedad de estado con valores iniciales:
class Menus extends Component{ state: { /* state properties */ }; }
-
Cuando declara funciones en el método de representación, siempre crea una nueva cada representación que tiene algún costo y no está optimizada. Es mejor si usas curry:
handleClick = selected => () => { /* handle click */ } render () { // ... coffees.map( coffee => // ... <div onClick={ this.handleClick(coffee) }> // ... }
-
Puede redirigir con
history.replace
ya que envolvió su componente conwithRouter
y eso es útil aquí porque redirige al hacer clic y deshacerse del métodorenderCoffee
:handleClick = selected => () => this.setState( { isLoading: true}, () => setTimeout( () => { const { history } = this.props; this.setState({ isLoading: false }); history.replace(`/${coffee}`); } , 5000) );
Dado que
Redirect
reemplaza la ruta y creo que desea que el cambio normal de página no reemplace, sugiero usar
history.push
lugar.
basado en su código, podría hacerlo de dos maneras.
onClick=(event) => this.gotoCoffee(event.target.value)
Este se parece al enfoque que desea.
onClick=() => this.gotoCoffee(c)
c
estaría relacionado con su artículo en la matriz.