Reputation: 1390
Working on an application for Android using React Native, I stumbled upon a strange issue.
There is a SearchBar to perform a name search on a database. The result should be rendered. In the code below, you see I set the variable with the result in useEffect()
-Hook after defining the variable outside using useRef()
: let returnValue = useRef('No results');
So I expected that if I type a name, such as "Mike", the database result (stored in returnValue.current
) will be rendered immediately after submitting.
But no.
Actually, the result "Mike" is rendered after I open the Search Bar again and delete the last character, leaving "Mik". Then the application renders "Mike", as if it was exactly one step behind.
After searching for the issue, I just found async "problems" with useState()
-Hook, but not with useEffect()
. When I do console.log()
the useState()
, everything is set correctly. Even inside useEffect()
- logging returnValue.current
always gives the correct result in the correct time. The one single issue is the rendering of returnValue.current
with that strange delay.
Is someone able to demystify this behavior?
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>
);
};
Upvotes: 0
Views: 1211
Reputation: 38787
Try replacing the useRef
hook with just useState
hook. It should be enough as returnValue
is just a computed value derived from 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>
);
};
Upvotes: 1