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

React: rendering a variable set in useEffect() is always one step behind

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

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

所以我希望如果我输入一个名字,比如"Mike",数据库结果(存储在returnValue.current)将在提交后立即呈现。

但是没有。

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

搜索问题后,我发现异步 "problems" 与 useState()-Hook,但没有与 useEffect()。当我执行 console.log()useState() 时,一切都设置正确。即使在 useEffect()- logging 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>
  );
};

尝试用 useState 挂钩替换 useRef 挂钩。它应该足够了,因为 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>
  );
};