Why my input closes when new Component is showing? - react-native

I have a a Header there is a text input and I have a Main component there are the list of products of the searched text.
So If the input text is empty then I want to show him his last searched things. If he type anything then I want to show him the products with the same name as the input text. I make a condition like this:
const SearchRoot = ({ }: ISearchRoot) => {
const searchParam = useSelector((state: RootState) => state.Search.searchParam);
return (
<>
{ searchParam.length > 0 ?
<SearchList />
:
<LatestHistory />
}
</>
)
}
So if I type anything then my input are closing automatically. But if I press again the keyboard and typing then its not closing and working. So its only happend when the component changing from <LatestHistory to <SearchList only one time. So how can I make my keyboard always open when the component is chaning ?
Search
const Search = () => {
return (
<View style={s.container}>
<StatusBar backgroundColor='#fff' />
<SearchHeader />
<SearchRoot />
</View>
)
}
SearchHeader
const SearchHeader = () => {
const navigation = useNavigation<NativeStackNavigationProp<RootStackParams>>();
const dispatch = useDispatch();
// filters
const [searchText, setSearchText] = useState<string>('');
const handleChangeText = (e: string) => {
dispatch(setSearch({searchParam: e}));
setSearchText(e)
};
const handleClearText = () => {
dispatch(setSearch({searchParam: ''}));
setSearchText('');
};
const handlePressSeach = () => {
searchText.length > 0 &&
navigation.navigate('Searched', {
searchText,
searchType: tabType
});
};
const handleGoBack = () => navigation.goBack();
return (
<View style={s.container}>
<View style={s.header}>
<View style={s.backContainer}>
<GoBackIcon
onPress={handleGoBack}
color='#555'
/>
</View>
<View style={s.inputContainer}>
<SearchInput
value={searchText}
onChangeText={handleChangeText}
onPressSearched={handlePressSeach}
onPressClearTextField={handleClearText}
autoFocus={true}
style={s.sInput}
/>
</View>
</View>
</View>
)
}
SearchRoot
const SearchRoot = ({ }: ISearchRoot) => {
const searchParam = useSelector((state: RootState) => state.Search.searchParam);
return (
<>
{ searchParam.length > 0 ?
<SearchMain />
:
<Text>Vorschläge</Text>
}
</>
)
}
I am very thankful for your help!!

I just read your problem statement and matched it with your Code.
✦First of all your sequence is wrong.
✦First Comes the Previous Searched Result.
✦Then when you press it then it should clear the previous search result.
✦Then On text change Property
✦Then OnPress Searched
Maybe that's Why it closes. If that's Not the case then!
(In my Opinion)
when then you click the search bar the previously searched result disappears and this is done by the function handleClearText. Now When the Function Completes it's Functionality the search bar closes. The Second time you open the search bar it works as their's no Previous Search result to clear.
So the Problem is with the handleClearText function.
My Proposed Solution in case 2 is
We can call the search method again (at the end) of handleClearText function.
then it will re open the search bar with the new state.
I hope it resolves your issue, Regards...

first of all const [searchText, setSearchText] = useState(''); this whole logic move to parent class search and from there pass it to siblings to avoid any confusion.whenever re-rendering happens values will be passed correctly to child.I think somewhere that is going wrong.Either use state and prop drilling or store, dont mix it up.

Related

I'm trying to call a function defined on the web, but nothing happens

I'm trying to open a simple page with React Native WebView.
It's a single page web, and when you do a search, it prints out some information about your search.
After that, if you want to search again, press the back button on the device to move to the search box.
Because it is a single page, I cannot use goBack, so I created a function called cancel.
The problem is that when I click the device's back button, the function called cancel defined on the web is not executed.
The cancel function deletes the searched information and returns to the search window.
I will upload my code.
Please advise.
export default function App() {
const webviewRef = useRef(null);
const backAction = () => {
setBackTapping((prev) => prev += 1);
webviewRef.current.injectJavaScript('window.cancel()')
return true;
}
useEffect(() => {
const timer = setInterval(() => {
setBackTapping(0)
}, 1000)
return () => clearInterval(timer);
}, [])
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);
return () => backHandler.remove()
}, [])
useEffect(() => {
if(backTapping >= 2){
return BackHandler.exitApp();
}
},[backTapping])
return (
<KeyboardAvoidingView style={{ flex: 1 }}>
<StatusBar hidden />
<WebView
ref={webviewRef}
textZoom={100}
originWhitelist={['*']}
javaScriptEnabled
source={{ uri: 'myhome.com'}}
startInLoadingState={true}
/>
</KeyboardAvoidingView>
);
}
Expected behavior:
The cancel function is executed, all open windows are closed, and you are returned to the search window.
in my case, calling is wrong.
instead of :
webviewRef.current.injectJavaScript('window.cancel()')
use :
const generateOnMessageFunction = (data) => `
(function(){
window.dispatchEvent(new MessageEvent('message',{data: ${JSON.stringify(data)}}));
})()
`;
webviewRef.current.injectJavaScript(generateOnMessageFunction('cancel'));
detail referance :
https://github.com/react-native-webview/react-native-webview/issues/809

How can I get dynamically updating Flatlist in react-native?

I am working on a Ble device, I used react-native-ble-manager, useState, and useEffect to get notifications from event listeners and try to show them on App. I have posted a part of the code related to incoming data.
const [dataTest, setDataTest] = useState([]);
const handleUpdateValueForCharacteristic = (data) => {
setDataTest(data.value); // data.value is a object incoming every one second from device.
};
useEffect(() => {
bleManagerEmitter.addListener("BleManagerDidUpdateValueForCharacteristic", handleUpdateValueForCharacteristic);
}, []);
const renderData = (item) => {
return (
<View>
<Text>{item}</Text>
</View>
);
};
return (
<>
<SafeAreaView>
<FlatList
data={dataTest}
renderItem={({ item }) => renderData(item)} />
</SafeAreaView>
</>
);
This code, It showing only the latest events are coming from the device. But I need a list that contains older and new upcoming events so that I can scroll the events data.
You almost got it. Your handleUpdateValueForCharacteristic function is the issue here.
Currently, you are overwriting the previous state (thus forgetting al previous values) by setting the state to new data.value. You want to append incoming data to the existing results, instead of overwriting them.
So you could do something like this:
const [dataTest, setDataTest] = useState([]);
const handleUpdateValueForCharacteristic = (data) => {
const prevData = [...dataTest];
prevData.unshift(data.value); // unshift adds elements to the beginning of an array
// instead of using the incoming data.value, we use the prevData array since we just added the new value to it.
setDataTest(prevData);
};
I asked the same question on Reddit. The solution provided by __o_0 worked for me:
const handleUpdateValueForCharacteristic = useCallback(({value, peripheral, characteristic, service}) => {
const data = bytesToString(value);
setDataTest(prevData => {
return [...prevData, data]});},[])
const renderItem = ({item, index}) => {
return (
<View><Text>{index}</Text></View>);};
const keyExtractor=(item,index,) => \${index}`;`
return (
<>
<SafeAreaView>
<FlatList
data={dataTest}
renderItem={renderItem}
keyExtractor={keyExtractor}
/>
</SafeAreaView>
</>
);
};

Problem saving state with set in react hook when loading data from an API?

My idea is that when someone put a text in my TextInput and clicks submit then botToc() will save the data from that API search in result and show the result in a FlatList.
But I have a problem saving data with hooks. The thing is that my setResult is not working properly because it does not save well the data from the API query. botToc() is supposed to fetch data from an API and its doing that fine but then I loop through the fetched data and I save what I want in result using setResult.
The thing is that when I click the button that uses botToc2() ( after clicking the button that uses botToc() ) my console.log(result) shows only the data of the last element and if I use again botToc() and I click one more time botToc2() I get that last element duplicated in the result array.
How can I fix this?
export default function TestScreen () {
const [querypar, setQuerypar] = useState('');
const [apipar, setApipar] = useState('https://API/search?q=');
const [result, setResult] = useState([]);
const ItemView = ({item}) => {
return (
<View>
<Text>
{item[0]+ ' ' + item[1]}
</Text>
</View>
);
};
function botToc() {
let query = apipar+querypar; //'https://API/search?q='+'TextInputText'
let cargador = [];
fetch(query) //'https://API/search?q=TextInputText'
.then( res => res.json() )
.then( res => {
// (res.results) = [{price:x,title:x},{price:x,title:x},{price:x,title:x}] structure of (res.results)
(res.results).forEach( (element) => {
cargador.push(element.title);
cargador.push(element.price); //cargador=[x,x]
setResult([...result, ...cargador]); //result=[[x,x],[x,x]...]
cargador.length = 0; //cargador=[]
});
})
};
function botToc2() {
console.log(result); //my console should return [[x,x],[x,x],[x,x],[x,x],[x,x],[x,x],...]
};
return (
<View View style={styles.container}>
<TextInput placeholder="write here" onChangeText={(val) => setQuerypar(val)} />
<View>
<Button onPress={botToc} title="Submit"/>
<Button onPress={botToc2} title="Submit"/>
<FlatList
data={result}
renderItem={ItemView}
keyExtractor={(item, index) => index.toString()}
/>
</View>
</View>
);
};
Calling setResult() multiple times in the forEach function should be avoided. In that case your botToc() function should look like this:
function botToc() {
let query = apipar+querypar; //'https://API/search?q='+'TextInputText'
let cargador = [];
fetch(query) //'https://API/search?q=TextInputText'
.then( res => res.json() )
.then( res => {
// (res.results) = [{price:x,title:x},{price:x,title:x},{price:x,title:x}] structure of (res.results)
(res.results).forEach( (element) => {
cargador.push([element.title, element.price]); //cargador=[x,x]
});
})
setResult(cargador);
};
This should do the job for you.

Passing state via react navigation

UPDATE
OK, so I got a bit further. I made it work, but it's ugly and buggy :)
What I want to do - I'm trying to share a search field between two screens. Almost identical to the way Yelp works, actually. The first screen is the map with the search field on top. When you click the search field, it should move into the second search screen where you type the search term or select some default search category.
I have an expo app with bottom tab navigator (TS). Let me paste only the important parts:
export default function BottomTabNavigator() {
return (
<BottomTab.Navigator initialRouteName="Search">
<BottomTab.Screen
name="Search"
component={SearchNavigator}
...
and then
const SearchStack = createStackNavigator<SearchParamList>();
function SearchNavigator() {
return (
<SearchStack.Navigator headerMode="none">
<SearchStack.Screen name="SearchScreen" component={SearchScreen} />
<SearchStack.Screen name="SearchFocusScreen" component={SearchFocusScreen} />
</SearchStack.Navigator>
);
}
SearchScreen
const [searchQuery, setSearchQuery] = useState('');
// If we returned from the second search screen with a search term, set it in state.
useEffect(() => {
if (route.params) {
setSearchQuery(route.params.searchTerm);
}
}, [route]);
const searchRef = useRef(null);
const onFocus = () => {
// If we have a search term, pass it to screen 2, then blur the input so we don't loop back.
if (route.params) {
setSearchQuery(route.params.searchTerm);
navigation.navigate('SearchFocusScreen', { searchTerm: searchQuery });
} else {
navigation.navigate('SearchFocusScreen');
}
searchRef.current.blur();
};
return (
<View>
<MapView ... />
<View>
<SafeAreaView>
<Searchbar
ref={searchRef}
placeholder="Search"
value={searchQuery}
onSubmitEditing={() => {
initSearch({ searchQuery, region });
}}
onFocus={onFocus}
/>
Second search screen
const [searchQuery, setSearchQuery] = useState('');
const searchRef = useRef(null);
useEffect(() => {
searchRef.current.focus();
if (route.params) {
setSearchQuery(route.params.searchTerm);
}
}, [route]);
return (
<SafeAreaView>
<View>
<Searchbar
ref={searchRef}
placeholder="Search"
onChangeText={(query) => setSearchQuery(query)}
value={searchQuery}
onSubmitEditing={() => {
navigation.navigate('SearchScreen', { searchTerm: searchQuery }); // Pass the search query back to Search page 1
}}
...
Does it work? Yes. But it feels wrong. Also - there's an issue that when I type with an error into the search field in search screen 2, I get auto-correct suggestions. If I then quickly click Search, it auto corrects the typo after sending the error to search screen 1. And it's visible.
If you guys have a better strategy to achieve this, please do share.
Because navigate is an object, useEffect only compares the reference value. If you would like changes to that object to trigger a useEffect, I would look at https://github.com/kentcdodds/use-deep-compare-effect

react-native reseting TextInput after ClearTextOnFocus

I am building a form in React-Native and have set ClearTextOnFocus to true as it is easier to handle dynamic formating for editing.
I am trying to add a reset function by setting all local state to the redux store, but if the user has not typed anything in a selected TextInput, the local state has not changed, and react native does not re-render the TextInput; leaving it blank.
Anyone have any thoughts on how I can unclear the TextInput or force React to re-render. Code is a work in progress, but here are the relevant bits.
Thanks
class GoalScreen extends Component {
componentWillMount = () => this.setPropsToState();
onReset = () => {
this.setPropsToState();
}
onChange = text => this.setState({ [text.field]: text.input });
setPropsToState = () => {
const { name } = this.props.goal;
this.setState({ name });
};
render() {
const { name } = this.state;
return (
<View style={styles.screenContainer}>
<Text style={styles.text}> Name </Text>
<TextInput
placeholder="a brand new bag"
keyboardType="default"
autoCorrect={false}
style={styles.inputField}
clearTextOnFocus
onChangeText={text => this.onChange({ input: text, field: 'rate' })}
value={name}
/>
</View>
}
}
So, I'm not using Redux, and my use case might be a bit different than yours, but I thought my solution might still be relevant here, if only to confirm that (after hours of wrangling with this) it appears that passing true to the clearTextOnFocus prop prevents further updates to a TextInput component.
I tried every conceivable workaround (like setNativeProps(), forceUpdate()) but nothing worked, so I ended up having to basically write my own logic for clearing and resetting the input text.
This component should 1) clear input text on focus and then 2) reset it to its previous value if the user hasn't pressed a key:
class ResettableInput extends Component {
state = {
Current: this.props.value,
Previous: ""
};
KeyPressed = false;
//cache current input value for later revert if necessary, and clear input
onFocus = () => {
this.setState({ Previous: this.state.Current, Current: "" });
};
//record whether key was pressed so input value can be reverted if necessary
onKeyPress = () => {
this.KeyPressed = true;
};
onChangeText = text => {
this.setState({ Current: text });
};
//if no key was pressed, revert input to previous value
onBlur = () => {
if (!this.KeyPressed) {
this.setState({ Current: this.state.Previous, Previous: "" });
}
};
render = () => {
return (
<TextInput
onChangeText={this.onChangeText}
value={this.state.Current}
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyPress={this.onKeyPress}
/>
);
};
}