反应:在useEffect()中渲染变量集总是落后一步

时间:2020-02-09 17:46:20

标签: reactjs react-native react-hooks use-effect

在使用React Native开发适用于Android的应用程序时,我偶然发现了一个奇怪的问题。

有一个SearchBar可以在数据库上执行名称搜索。结果应该被渲染。在下面的代码中,您看到使用useEffect()在外部定义变量后,我在useRef()-Hook中设置了变量:let returnValue = useRef('No results');

因此,我希望如果键入一个名称(例如“ Mike”),则数据库结果(存储在returnValue.current中)将在提交后立即呈现。

但是没有。

实际上,当我再次打开搜索栏并删除最后一个字符并保留“ Mik”后,将呈现结果“ Mike”。然后,应用程序呈现“ Mike”,就好像它只落后了一步。

在搜索问题之后,我刚刚发现与useState()-Hook异步,但没有与useEffect()异步的“问题”。当我执行console.log() useState()时,一切设置正确。即使在useEffect()内-记录returnValue.current也总是在正确的时间给出正确的结果。一个单一的问题是returnValue.current的渲染具有这种奇怪的延迟。

有人能够揭露这种行为的神秘色彩吗?

import ...

const SearchBar = props => {
  let [inputValue, setInputValue] = useState(null);
  let returnValue = useRef('No results');

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

  useEffect(() => {
    // setting database schema
    const personsSchema = {
      name: 'realm',
      properties: {
        name: 'string?',
        color: 'string?',
      },
    };

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

    // doing the database query
    const allPersons = database.objects('realm');
    let resultArray = [];
    const query = inputValue;
    const queryResult = inputValue
      ? allPersons.filtered("name == '" + query + "'")
      : 'default';
    resultArray = Array.from(queryResult);
    const personNamesArray = resultArray.map(value => value.name);
    const personNames = personNamesArray.toString();
    returnValue.current = personNames;
    // logging always correct
    console.log('person name: ', personNames);
  }, [inputValue]);

  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>
      {isText && (
        <View>
          <Text>{props.text}</Text>
        </View>
      )}
      //returnValue.current is rendered with delay
      {returnValue && <Text>{returnValue.current}</Text>}
    </View>
  );
};

1 个答案:

答案 0 :(得分:1)

尝试仅用useRef钩替换useState钩。由于returnValue只是从inputValue得出的计算值,就足够了:

import ...

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

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

  useEffect(() => {
    // setting database schema
    const personsSchema = {
      name: 'realm',
      properties: {
        name: 'string?',
        color: 'string?',
      },
    };

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

    // doing the database query
    const allPersons = database.objects('realm');
    let resultArray = [];
    const query = inputValue;
    const queryResult = inputValue
      ? allPersons.filtered("name == '" + query + "'")
      : 'default';
    resultArray = Array.from(queryResult);
    const personNamesArray = resultArray.map(value => value.name);
    const personNames = personNamesArray.toString();
    setReturnValue(personNames);
  }, [inputValue]);

  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>
      {isText && (
        <View>
          <Text>{props.text}</Text>
        </View>
      )}
      {returnValue && <Text>{returnValue}</Text>}
    </View>
  );
};