Open huberf opened 7 years ago
Here is a video of the app experiencing the issue: https://goo.gl/photos/jgekQ4R9Hhs3uKNb8
@huberf Can you show me your code? Ensure these function beforeFocus, onFocus, afterFocus return a Promise. Checkout details example: https://github.com/crabstudio/react-native-atoz-listview/blob/master/example/src/screens/Contacts/Home.js#L162
@huberf anything new?
The functions are executing and to test the search, I have had each one use the example code inside, so each one calls resolve(); after execution is finished. If one looks at the logs, after initially tapping the search bar, it looks as follows. [code] beforeFocus onFocus afterFocus beforeFocus onFocus afterFocus [/code] These infinitely repeat. One thing that differs from the example is my functions scope are inside of a function placed in the render method. I'll try making them functions of the view object later today, and report if that affects the outcome.
However, the problem does appear as soon as the view is loaded, and then prevents the keyboard from working across the entire app. Which makes it seem that the search module is instructing keyboard shutdown as soon as keyboard launch is initiated.
Can you show me your source code? My example don't get loop problem. Please take a look: https://github.com/crabstudio/react-native-atoz-listview/blob/master/example/src/screens/Contacts/Home.js#L162
Here is my source code. Basically, it initially renders the search bar and loading text, and then when the recommendations from the API arrive, it refreshes it with the search bar still at the top and the recommendation elements below it.
var React = require('react');
var ReactNative = require('react-native');
var t = require('tcomb-form-native');
import { Actions } from 'react-native-router-flux';
import Tabs from 'react-native-tabs';
var {
AppRegistry,
ScrollView,
AsyncStorage,
ListView,
StyleSheet,
Text,
View,
WebView,
TouchableHighlight,
RefreshControl,
Alert,
TabBarIOS,
StatusBar,
} = ReactNative;
var {
Component
} = React;
import {
Divider,
ListBasic,
} from 'react-native-uikit';
// Setup search form
/*
var Form = t.form.Form;
var Search = t.struct({
query: t.String
});
const options = {
};
*/
// var ProfileRec = require('./elements/profileRec');
var profileCard = require('./elements/profileCard');
var careerCard = require('./elements/careerCard');
import AtoZListView from 'react-native-atoz-listview';
import SearchBar from 'react-native-search-box';
var api = require('./api');
// Components
var tabBar = require('./elements/tabBar');
var story = require('./elements/story');
// Series number
var series = 1;
export default class loginPage extends Component {
constructor(props){
super(props);
this.state = {page:'explore', data: {
"A": [
{
"name": "Anh Tuan Nguyen",
"age": 28
},
{
"name": "An Nhien",
"age": 20
},
],
"Z": [
{
"name": "Zue Dang",
"age": 22
},
{
"name": "Zoom Jane",
"age": 30
},
]
}};
// Ensure recommendations load on launch
this.props.justJumped = true;
var tips = [];
}
componentDidMount() {
}
renderRow = (item, sectionId, index) => {
return (
<TouchableHightLight
style={{
height: rowHeight,
justifyContent: 'center',
alignItems: 'center'}}
>
<Text>{item.name}</Text>
</TouchableHightLight>
);
}
beforeFocus = () => {
return new Promise((resolve, reject) => {
console.log('beforeFocus');
resolve();
});
}
onFocus = (text) => {
return new Promise((resolve, reject) => {
console.log('onFocus', text);
resolve();
});
}
afterFocus = () => {
return new Promise((resolve, reject) => {
console.log('afterFocus');
resolve();
});
}
onSearch = (text) => {
return new Promise((resolve, reject) => {
Alert.alert(text);
Actions.searchPage({justJumped: true, queryString: text});
resolve();
})
}
onCancel = () => {
return new Promise((resolve, reject) => {
console.log('onCancel');
resolve();
})
}
render() {
var showRecs = (data) => {
var toShowInfl = [];
infl = data.influencers;
for(var i = 0; i< infl.length;i ++) {
toShowInfl.push({
userId: infl[i].userId,
uuid: infl[i].uuid,
name: infl[i].name,
description: infl[i].bio,
profile: infl[i].profileImage,
isFollowing: false,
});
}
var toShowJobs = [];
jobs = data.jobs;
for(var i = 0; i < jobs.length;i ++) {
toShowJobs.push({
name: jobs[i].name,
description: jobs[i].description,
meanAnnualWage: jobs[i].meanAnnualWage,
employment: jobs[i].employment,
blsId: jobs[i].blsId,
});
}
Actions.refresh({ recommendationList: { influencers: toShowInfl, careers: toShowJobs }, justJumped: false });
}
var getRecs = () => {
if (this.props.justJumped != false || this.state.justJumped != false) {
this.props.justJumped = false;
this.state.justJumped = false;
api.loadUserRecommendations(showRecs);
}
}
var allItems = [];
if (this.props.justJumped == false || this.state.justJumped == false) {
if (this.props.recommendationList) {
allItems = allItems.concat([{ type: 'search' }]);
allItems = allItems.concat(this.props.recommendationList.careers.map((e, index) => {
e.type = 'job';
return e;
}));
allItems = allItems.concat(this.props.recommendationList.influencers.map((e, index) => {
e.type = 'infl';
return e;
}));
} else {
getRecs();
}
} else {
allItems = allItems.concat([{ type: 'search' }]);
allItems = allItems.concat([{ type: 'loading' }]);
}
this.state.allRecommendations = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state.allRecommendations = this.state.allRecommendations.cloneWithRows(allItems);
// Finally load recs
getRecs();
// Prevent recs from loading without explicit command
this.props.justJumped = false;
this.state.justJumped = false;
var renderCareer = (data) => {
Actions.careerPage({details: data});
}
var renderInfluencer = (data) => {
Actions.influencerPage({details: data});
}
var renderJobRow = (rowData) => {
return (
<View onPress={() => renderCareer(rowData)}>
<Text onPress={() => renderCareer(rowData)}>{rowData.name} - {rowData.description.substring(0, 60)}...</Text>
<Divider style={{paddingTop: 20}} />
</View>
);
}
var renderInflRow = (rowData) => {
return profileCard({rowData});
//return ProfileRec(rowData.profile, rowData.name, rowData.description, () => renderInfluencer(rowData));
/*return (<View onPress={() => renderInfluencer(rowData)}>
<Text onPress={() => renderInfluencer(rowData)}>{rowData.name} - {rowData.description}</Text>
<Divider style={{paddingTop: 20}} />
</View>)*/
}
var renderRowAll = (rowData) => {
if (rowData.type == 'job') {
return careerCard(rowData);
/*return (<View onPress={() => renderCareer(rowData)}>
<Text onPress={() => renderCareer(rowData)}>{rowData.name} - {rowData.description.substring(0, 60)}...</Text>
<Divider style={{paddingTop: 20}} />
</View>)*/
} else if (rowData.type === 'infl') {
return profileCard({rowData});
// return ProfileRec(rowData.profile, rowData.name, rowData.description, () => renderInfluencer(rowData));
} else if (rowData.type === 'search') {
return (<View style={{flex: 1}}>
<SearchBar
ref='search_box'
placeholder='Search'
beforeFocus={this.beforeFocus}
onFocus={this.onFocus}
afterFocus={this.afterFocus}
onSearch={this.onSearch}
onCancel={this.onCancel}
/>
</View>);
} else if (rowData.type === 'loading') {
return (<Text style={{paddingTop: 0, alignSelf: 'center'}}>Loading...</Text>)
}
}
this.state.refreshing = false;
return (
<View style={styles.container}>
<View style={styles.container}>
<ListView
style={{flex: 1, alignSelf: 'stretch'}}
dataSource={this.state.allRecommendations}
renderRow={(rowData) => renderRowAll(rowData)}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={() => {this.state.refreshing = true; Actions.refresh({justJumped: true}); this.state.refreshing = false;}}
/>
}
/>
</View>
{tabBar(this.state.page)}
</View>
);
}
};
var styles = StyleSheet.create({
/*
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20,
backgroundColor: '#ffffff',
},
*/
container: {
padding: 0,
flex: 1,
alignSelf: 'stretch',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 30,
alignSelf: 'center',
marginBottom: 30
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
button: {
width: 200,
height: 36,
backgroundColor: '#48BBEC',
borderColor: '#48BBEC',
borderWidth: 1,
borderRadius: 8,
marginBottom: 10,
alignSelf: 'stretch',
justifyContent: 'center'
},
});
I have isolated the module code causing the issue, and been able to prevent the catastrophic keyboard failure by commenting out this line https://github.com/crabstudio/react-native-search-box/blob/master/index.js#L217
Do you know what would cause the collapseAnimation function to autorun as soon as the view containing the search box is loaded? For now, I'll just use the locally patched module, but am happy to help with any module fixes or restructuring of my own code.
I wonder if this might be the module code causing the issue https://github.com/crabstudio/react-native-search-box/blob/master/index.js#L68. I can't test this right now but will look into it later unless you can get to it sooner.
Thanks for helping to assist!
I have the same issue. As soon as I click on the search box afterFocus
, beforeFocus
and onFocus
are called in an infinite loop....
I am testing on android
@ramsestom Did commenting out the line of code at https://github.com/crabstudio/react-native-search-box/blob/master/index.js#L217 resolve the issue? You can do so in the node_modules/ directory. If this resolves it for you too, then it will help me diagnose the issue further and hopefully, submit a PR to fix it.
yes If I do
collapseAnimation = ( isForceAnim = false ) => {
return new Promise((resolve, reject) => {
/*
Animated.parallel([
( ( this.props.keyboardShouldPersist === false ) ? Keyboard.dismiss() : null ),
Animated.timing(
this.inputFocusWidthAnimated,
{
toValue: this.contentWidth - 10,
duration: 200
}
).start(),
Animated.timing(
this.btnCancelAnimated,
{
toValue: this.contentWidth,
duration: 200
}
).start(),
( ( this.props.keyboardShouldPersist === false ) ?
Animated.timing(
this.inputFocusPlaceholderAnimated,
{
toValue: this.middleWidth - this.props.placeholderCollapsedMargin,
duration: 200
}
).start() : null ),
( ( this.props.keyboardShouldPersist === false || isForceAnim === true ) ?
Animated.timing(
this.iconSearchAnimated,
{
toValue: this.middleWidth - this.props.searchIconCollapsedMargin,
duration: 200
}
).start() : null ),
Animated.timing(
this.iconDeleteAnimated,
{
toValue: 0,
duration: 200
}
).start(),
Animated.timing(
this.shadowOpacityAnimated,
{
toValue: this.props.shadowOpacityCollapsed,
duration: 200
}
).start(),
]);
this.shadowHeight = this.props.shadowOffsetHeightCollapsed;
*/
resolve();
});
}
That works
@huberf @ramsestom @anhtuank7c
I find a fix for it.
around line 273
<Animated.View
ref="searchContainer"
style={
[
styles.container,
his.props.backgroundColor && { backgroundColor: this.props.backgroundColor }
]}
onLayout={this.onLayout}
>
simply remove the line:
onLayout={this.onLayout}
then, the glitch is gone. But you still have the expand/collapse animation.
btw, tested both on android and ios
Hi @huberf @anhtuank7c @ramsestom @cyveros @smtlaissezfaire I have same keyboard loop issue. Tried above solutions but never fixed.
<Search
inputHeight={Metrics.navbarHeight - 12}
ref="search_box"
onSearch={this.onSearch}
onChangeText={(text) => {
this.setState(
{
searchText: text
},
() => this.reload()
);
}}
value={this.state.searchText}
afterCancel={(text) => {
this.setState(
{
searchText: '',
lastVisible: null
},
() => {
this.props.navigation.state.params.onGoBack();
this.props.navigation.goBack();
}
);
}}
afterDelete={() => {
this.setState(
{
searchText: ''
},
() => this.reload()
);
}}
onFocus={this.onFocus}
beforeFocus={this.beforeFocus}
afterFocus={this.afterFocus}
backgroundColor="#1c2127"
returnKeyType="done"
autoCapitalize="words"
// autoFocus={Platform.OS === 'ios' ? true : false}
autoFocus={true}
keyboardShouldPersist={false}
/>
Here is my video what is happening.
Anyone have solution for me?
I'm trying to integrate the search element into the Android version of my React Native app, but when the view containing the search element is loaded, no matter where I navigate in the app when the keyboard attempts to launch, it immediately closes back. In regular text inputs, this is where the action ends, but in the search element, it creates an infinite loop where the beforeFocus, onFocus, and afterFocus events all fire one after the other in a never ending cycle. The app becomes unresponsive to touch, and the "Cancel" button can't be operated.
Does anyone know what might be causing this? Initially, I thought it might be that the search bar was inside a ListView, but the same behavior happened when it was placed outside of it. I've tried debugging it for the past couple hours, but can't seem to find the source of the issue.
Thanks in advance!