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>
);
};
在使用 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>
);
};