Aquí está mi confusión:

     constructor(props) {
    super(props);
  }

  hookNav(){
    console.log("work");
  }

  renderItem({ item, index }) {
    return (
      <TouchableOpacity style={{ margin: 9 }} onPress={this.hookNav}>
        <View
          style={{
            flex: 1,
            minWidth: 170,
            maxWidth: 223,
            height: 280,
            maxHeight: 280,
            borderRadius: 10,
          }}
        >
          <ImageBackground
            source={{
              uri: "https://picsum.photos/170/223",
            }}
            style={{ flex: 1 }}
            imageStyle={{ borderRadius: 10 }}
          >
            <Text
              style={{
                color: "white",
                position: "absolute",
                bottom: 20,
                right: 10,
                fontWeight: "bold",
                textShadowColor: "black",
                textShadowOffset: { width: -1, height: 1 },
                textShadowRadius: 10,
              }}
            >
              Topic
            </Text>
          </ImageBackground>
        </View>
      </TouchableOpacity>
    );
  }

  render(){
    return (
      <View style={{ marginBottom: 80, backgroundColor: "white" }}>
        <Header
          centerComponent={{ text: "test", style: { color: "orange" } }}
          rightComponent={{ icon: "add", color: "orange" }}
        />
        <FlatList
          numColumns={2}
          onEndReachedThreshold={0}
          onEndReached={({ distanceFromEnd }) => {
            console.debug("on end reached ", distanceFromEnd);
          }}
          contentContainerStyle={styles.list}
          data={[
            { key: "a" },
            { key: "b" },
            { key: "c" },
            { key: "d" },
            { key: "e" },
            { key: "f" },
          ]}
          renderItem={this.renderItem}
          ListHeaderComponent={
            <Text
              style={{ padding: 10, fontWeight: "bold" }}
            >
              Your Topics
            </Text>
          }
        />
      </View>
    );
  }
}

En este código anterior, cuando se navega a esta página, se produce un error inmediato de que this.hookNav no está definido.

Sin embargo, cuando pongo la función hookNav dentro de un objeto de estado, así

constructor(props) {
    super(props);
    state = {
      hookNav : function() {
       console.log("work please");
      }
    }
  }

Y en onPress dentro de renderItem

const { hookNav} = this.state;
    return (
      <TouchableOpacity style={{ margin: 9 }} onPress={hookNav}>

Esto funciona según lo previsto.

Entiendo que render ya tiene acceso a los componentes state a través de this. Si renderItem puede acceder al objeto state, a través de this, ¿por qué no puede acceder a this.hookNav() directamente? ¿Por qué la función necesita estar encapsulada en un objeto para que esto funcione?

Gracias.

0
William Allen 27 jun. 2020 a las 02:28

2 respuestas

Es porque el valor de this dentro de cualquier función de Javascript depende de cómo se llamó esa función.

En su ejemplo que no funciona, this.hookNav está dentro del método renderItem. Entonces adoptará el this de ese método, que, como acabo de decir, depende de cómo se llame. La inspección adicional de su código muestra que su componente no llama directamente al método renderItem sino que lo pasa como un accesorio (llamado renderItem) del componente FlatList. Ahora no sé cómo se implementa ese componente (nunca he usado React Native), pero casi con certeza llama a esa función en algún momento, y cuando lo hace, no puede estar en el contexto de su componente. Esto se debe a que FlatList no puede saber de dónde proviene esa función, y cuando la llama, será tratada como una función "ordinaria", no en el contexto de ningún objeto en particular. Por lo tanto, su contexto this no será su instancia de componente, como se pretendía, sino que simplemente será el objeto global, que no tiene ningún método hookNav.

Este problema no ocurre en su segundo ejemplo porque simplemente accede a la función como parte de this.state: no se pasa ningún método, como renderItem en el ejemplo anterior, que depende de un Referencia this. Tenga en cuenta que este no será el caso si su hookNav se refiere a this dentro de él.

Tales problemas con el contexto this son muy comunes en React, pero afortunadamente hay dos soluciones generales que siempre solucionarán esto:

  1. Enlazar métodos en el constructor de su componente. Si el constructor, en el primer ejemplo, incluye la declaración this.renderItem = this.renderItem.bind(this);, entonces funcionaría bien. (Tenga en cuenta que esto se recomienda en el React oficial docs.) Es el "mejor" solución técnica (el rendimiento es un poco mejor que en la otra opción), pero implica un poco de repetitivo si tiene muchos métodos que necesitan ese enlace.

  2. Utilice las funciones de flecha para definir los métodos en su lugar. En lugar de renderItem({ item, index }) {...}, escríbalo como renderItem = ({ item, index }) => {...}. Esto funciona porque las funciones de flecha adoptan el this de su ámbito léxico, que será la clase misma, por lo que, en otras palabras, this siempre se referirá a la instancia del componente, como casi siempre desea.

No se preocupe si esto es confuso: el comportamiento de la palabra clave this en JS es un obstáculo común para los principiantes a JS, y con frecuencia suficiente para aquellos con más experiencia también. Hay una serie de buenas explicaciones en línea que lo desmitifican, de las cuales puedo recomendar especialmente este.

0
Robin Zigmond 26 jun. 2020 a las 23:46

Debe vincular el método a this de su clase. Una forma de hacerlo será como a continuación.

constructor(props){
  super(props);
  this.renderItem = this.renderItem.bind(this);
}
0
Rinkesh Golwala 26 jun. 2020 a las 23:47