首页 > 解决方案 > Do I really need the Hook useEffect() here?

问题描述

As a React Native newbie, I am working on an application for android. Basically, the app enables the user to filter a database according to specific search parameters.

Now I tried to convert a Class Component into a Functional component. The component just loads a prepopulated (Realm-) database, displays a search bar waiting for user input, performs a database query according to the user's input and shows the result.

In the "old" version, database setup was done during componentDidMount(), now I thought using the useEffect() Hook will do the job, since it works as a replacement for componentDidMount(). But I found that doing all the logic (which I actually wanted to put into the Hook) "naked" without any Hook works just fine.

I am so confused because of this behavior since I didn't expect that it would work this way. Surely I missed something. Googling unfortunately didn't give answers. Can you help me? Below is the code of the component I am talking of, SearchBar.jsThanks a lot for your help!

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);

This is the same component as Class Component before refactoring:

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),
);

标签: reactjsreact-nativereact-hooks

解决方案


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

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

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

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

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

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

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

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

推荐阅读