【问题标题】:Do I really need the Hook useEffect() here?我真的需要 Hook useEffect() 吗?
【发布时间】:2020-02-05 20:42:50
【问题描述】:

作为一名 React Native 新手,我正在开发一个适用于 android 的应用程序。基本上,该应用程序使用户能够根据特定的搜索参数过滤数据库。

现在我尝试将类组件转换为功能组件。该组件只是加载一个预填充的(Realm-)数据库,显示一个等待用户输入的搜索栏,根据用户的输入执行数据库查询并显示结果。

在“旧”版本中,数据库设置是在componentDidMount() 期间完成的,现在我认为使用useEffect() Hook 可以完成这项工作,因为它可以替代componentDidMount()。但我发现,在没有任何 Hook 的情况下“裸”执行所有逻辑(我实际上想放入 Hook)就可以了。

我对这种行为感到很困惑,因为我没想到它会以这种方式工作。我肯定错过了什么。不幸的是,谷歌搜索没有给出答案。你能帮助我吗?下面是我所说的组件的代码,SearchBar.js非常感谢您的帮助!

import React, {useState} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {connect} from 'react-redux';
import {Header, Icon, Item, Input} from 'native-base';
import Realm from 'realm';
import fs from 'react-native-fs';
import {setInputValueSearchBar} from '../../../actions';

const SearchBar = props => {
  let [inputValue, setInputValue] = useState(null);

  const sendInputValueToReduxStore = text => {
    setInputValue(text);
    props.setInputValueSearchBar(text);
  };

  // from here on, I wanted to use useEffect() ...
  const dogsSchema = {
    name: 'realm',
    properties: {
      name: 'string?',
      color: 'string?',
    },
  };

  let realm = new Realm({
    path: fs.DocumentDirectoryPath + '/default.realm',
    schema: [dogsSchema],
    readOnly: true,
  });

  const dogs = realm.objects('realm');
  let resultArray = [];

  const databaseRequest = () => {
    const query = inputValue;
    const result = inputValue
      ? dogs.filtered("name == '" + query + "'")
      : 'default';
    resultArray = Array.from(result);
    return resultArray.map(dog => (
      <Text className="dog" key={dog.name}>
        {dog.name}
      </Text>
    ));
  };

  //... until here

  const isText = props.text;

  return (
    <View>
      <Header searchBar rounded>
        <Item>
          <Icon name="ios-search" />
          <Input
            placeholder="Search"
            onChangeText={text => sendInputValueToReduxStore(text)}
            value={inputValue}
          />
        </Item>
      </Header>
      <View>{databaseRequest()}</View>
    </View>
  );
};

function mapStateToProps(state) {
  return {
    inputValue: state.inputValue,
  };
}

const mapDispatchToProps = {
  setInputValueSearchBar,
};

export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);

这和重构前的 Class Component 是同一个组件:

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {connect} from 'react-redux';
import {Header, Icon, Item, Input} from 'native-base';
import Realm from 'realm';
import fs from 'react-native-fs';
import {setInputValueSearchBar} from '../../../actions';

class SearchBar extends React.Component {
  state = {
    inputValue: null,
  };

  sendInputValueToReduxStore(text) {
    this.setState({inputValue: text});
    this.props.setInputValueSearchBar(text);
  }

  componentWillMount() {
    const dogsSchema = {
      name: 'realm',
      properties: {
        name: 'string?',
        color: 'string?',
      },
    };

    let realm = new Realm({
      path: fs.DocumentDirectoryPath + '/default.realm',
      schema: [dogsSchema],
      readOnly: true,
    });

    const dogs = realm.objects('realm');
    let resultArray = [];

    this.databaseRequest = () => {
      const query = this.state.inputValue;
      const result = this.state.inputValue
        ? dogs.filtered("name == '" + query + "'")
        : 'default';
      resultArray = Array.from(result);
      return resultArray.map(dog => (
        <Text className="dog" key={dog.name}>
          {dog.name}
        </Text>
      ));
    };
  }

  render() {
    const isText = this.props.text;
    return (
      <View>
        <Header searchBar rounded>
          <Item>
            <Icon name="ios-search" />
            <Input
              placeholder="Search"
              onChangeText={text => this.sendInputValueToReduxStore(text)}
              value={this.state.inputValue}
            />
          </Item>
        </Header>
        {isText && (
          <View>
            <Text>{this.props.text}</Text>
          </View>
        )}
        <View>{this.databaseRequest()}</View>
      </View>
    );
  }
}

function mapStateToProps(state) {
  return {
    inputValue: state.inputValue,
  };
}

const mapDispatchToProps = {
  setInputValueSearchBar,
};

export default withNavigation(
  connect(mapStateToProps, mapDispatchToProps)(SearchBar),
);

【问题讨论】:

  • 将代码直接放在组件函数体中意味着它将在每次组件重新渲染时执行,请记住这一点
  • @David,但是 useEffect() 钩子不是在每次渲染时都执行吗?
  • @RuntimeError: 对useEffect 的调用本身会,但useEffect 只会在您传递的依赖数组已更改时调用您传递的函数。

标签: reactjs react-native react-hooks


【解决方案1】:

当然,没有useEffect 也可以。但每次组件重新渲染时也会发生这种情况。这在您的应用程序中可能不是问题,原因如下:

  • 可能您的组件不会经常重新渲染,具体取决于您管理/更新的状态和位置
  • 也许这个逻辑是一种微不足道的资源消耗,如果经常执行也没关系

但如果情况并非如此,您可能需要衡量并确认正在发生的事情。在此处将一些日志记录输出添加到您的设置逻辑中,并在日志中观察它被调用的频率。

如果它没有被不断地重新调用(或者至少不是不合理的频繁),那么你当然可以保持这样的状态。请注意,它可能会开始根据该组件之外的因素不断地重新调用,任何会导致该组件重新渲染的因素。

如果它 被不断地重新调用,那么这就是 useEffect 的用途。您可以仅在组件第一次加载时执行此逻辑,因为没有依赖关系:

useEffect(() => {
  // your logic
}, []);

或者您可以添加依赖项以在这些依赖项更改时触发执行此逻辑。例如,可以在您的 inputValue 更改时重新调用它:

useEffect(() => {
  // your logic
}, [inputValue]);

【讨论】:

  • 非常感谢您的精彩解释!现在我明白了:)
【解决方案2】:

就性能而言,这不是一个好方法。

代码在每次渲染时执行,因此每次道具或状态值发生变化时,即使它与那段代码无关。

useEffect 钩子也执行每个渲染,但是允许你传递一个依赖列表(或者如果它没有依赖则一个空列表,那么它将执行一次)。那么代码只会在其中一个依赖发生变化时执行。

【讨论】:

    猜你喜欢
    • 2012-03-13
    • 2020-01-29
    • 2011-01-28
    • 1970-01-01
    • 2016-07-26
    • 1970-01-01
    • 2013-07-04
    • 2012-03-26
    • 1970-01-01
    相关资源
    最近更新 更多