When a click event is fired within my parent component I need to call the method closeMenu() from the SearchBar child component. I have tried a couple of different ways to do that but none of them are working. Does anyone know how to do this?
class Products extends Component {
constructor(props) {
super(props);
this.state = { closeMenu: false};
this.hideSearchBar = this.hideSearchBar.bind(this);
}
hideSearchBar(e) {
console.log('e: ', React.Children)
this.setState({closeMenu: true});
this.refs.SearchBar.closeMenu();
this.setState({closeMenu: false});
}
componentWillMount() {
this.props.dispatch(getProductList());
}
render() {
const {isLoading, products} = this.props.products;
if (isLoading) {
return <Loader isVisible={true}/>;
}
return (
<TouchableWithoutFeedback onPress={(e) => this.hideSearchBar(e)} style={{zIndex: 0}}>
<View style={styles.wrapper}>
<Header/>
<View style={styles.bodyWrapper}>
<ScrollView style={styles.scrollView}>
<ProductsContainer data={{productsList: { results: products }}}/>
</ScrollView>
<SearchBar ref="SearchBar" style={styles.searchBar} />
</View>
<Footer/>
</View>
</TouchableWithoutFeedback>
);
}
}
I also tried calling closeMenu() without refs:
hideSearchBar(e) {
this.setState({closeMenu: true});
this.SearchBar.closeMenu();
}
Here is the SearchBar component:
class SearchBar extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.suggestions = [];
}
componentWillUpdate(nextProps, nextState) {
console.log("COMPONENT WILL UPDATE");
console.log(nextProps);
console.log(nextState);
}
suggestionClick = (value) => {
}
getSuggestionText = (suggestion) => {
}
onChangeText = (value) => {
this.selectedSuggestion = false
this.props.dispatch(handleSearchItemText(value));
console.log(this.props.products.searchResults);
}
onFocus() {
const {height} = Dimensions.get('window');
this.setState({
contentOffset: {
x: 0,
y: height * 1 / 3
}
});
}
onBlur() {
this.setState({
contentOffset: {x: 0, y: 0}
});
}
closeMenu = () => {
this.props.products.searchResults = {};
}
componentWillReceiveProps() {
if (!this.props.closeMenu) {
this.props.closeMenu = false;
}
}
renderSearches = () => {
this.suggestions = this.props.products.searchResults;
const suggestionTexts = Object.keys(this.props.products.searchResults || {})
console.log(suggestionTexts);
if (!suggestionTexts.length) {
return null
}
// for android absolute element: https://github.com/facebook/react-native/issues/16951
// https://gist.github.com/tioback/6af21db0685cd3b1de28b84981f31cca#file-input-with-suggestions-L54
return (
<View
ref="suggestionsWrapper"
style={autoStyles.suggestionsWrapper}
>
{
this.suggestions.map((text, index) => (
<TouchableHighlight
key={index}
suggestionText={text}
activeOpacity={0.6}
style={autoStyles.suggestion}
onPress={this.suggestionClick(this.suggestions[text])}
underlayColor='white'
>
<Text style={autoStyles.suggestionText}>
{text}
</Text>
</TouchableHighlight>
))
}
</View>
)
}
render() {
const myIcon = (<Icon name="search" size={30} style={styles.searchIcon}/>);
const slidersIcon = (<Icon name="sliders" size={30} style={styles.slidersIcon}/>);
return (
<TouchableWithoutFeedback style={{zIndex: 0}}>
<View style={[styles.searchBar, this.props.style]}>
<View style={styles.searchContainer}>
<View>
{slidersIcon}
</View>
<View style={styles.search}>
<View style={styles.searchSection}>
{myIcon}
<TextInput
style={styles.input}
placeholder="Search"
placeholderTextColor="rgba(0,0,0,0.7)"
onChangeText={(searchString) => {
this.setState({searchString})
}}
underlineColorAndroid="transparent"
editable={true}
autoCorrect={false}
autoFocus={false}
autoCaptialize={'none'}
autoCorrect={false}
onChangeText={this.onChangeText}
enablesReturnKeyAutomatically={true}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
/>
</View>
</View>
</View>
{this.renderSearches()}
</View>
</TouchableWithoutFeedback>
);
}
}
There are some issues which you should avoid:
Never mutate props: this.props.something = {} is an anti-pattern. Think about props as data that your component does not own and which are not mutable. If they change then only because the parent passed new props.
Also you have multiple handlers in your SeachBar that are not bound to this but use this. It will not work. Use arrow functions if you want to use this or bind them in the constructor.
You should overthink the architecture of your app. Maybe it is a good idea to split the search bar and the result list into two separate components. When the user types something to search for update your redux store and display the results in a separate component that you only render if there are search results.
I'm affraid it would exceed the length of a stackoverflow answer to solve all these issues. Maybe you should go back to the basics first and do the really good redux tutorial.
Related
I am new to react native here I tried to convert class components to functional components, I have tried to pass ref in the functional component in several ways also I have used hooks to handle the state but I am unable to do so please help me out thanks in advance.
export default class AddClick extends Component {
constructor(props) {
super(props);
this.state = {
changeAnim: false,
};
}
componentDidMount() {
setTimeout(() => {
// handleScreenNavigation("OtpScreen", {});
this.setState({ changeAnim: true }, () => {
if (this.state.changeAnim) {
this.animation.play(48, 48);
}
});
}, 1500);
this.animation.play();
}
render() {
return (
<View style={styles.container}>
<View>
<Animation
ref={(animation) => {
this.animation = animation;
console.log("------#######");
}}
style={styles.imageStyle}
resizeMode="cover"
loop={true}
source={anim}
/>
</View>
</View>
);
}
}
here i have mentioned my attempt by functional component.
const AddClick = (props) => {
const [changeAnimation, setChangeAnimation] = useState(false)
useEffect(() => {
setTimeout(()=>{
setChangeAnimation(true),()=>{
if(changeAnimation){
animation.play(48,48)
}
}
},1500)
animation.play();
}, [])
return (
<View style={styles.container}>
<View>
<Animation
ref={(animation) => {
this.animation = animation;
console.log("------#######");
}}
style={styles.imageStyle}
resizeMode="cover"
loop={true}
source={anim}
/>
</View>
</View>
);
}
AppRegistry.registerComponent("AddClick", () => AddClick);
You cannot use this in a functional component. You can find the updated code here:
const AddClick = (props) => {
const [changeAnimation, setChangeAnimation] = useState(false)
let animation; // Create a local variable
useEffect(() => {
setTimeout(()=>{
setChangeAnimation(true),()=>{
if(changeAnimation){
animation.play(48,48)
}
}
},1500)
animation.play(); // Make sure to check if animation is defined before calling any methods
}, [])
return (
<View style={styles.container}>
<View>
<Animation
ref={(anim) => {
animation = anim;
console.log("------#######");
}}
style={styles.imageStyle}
resizeMode="cover"
loop={true}
source={anim}
/>
</View>
</View>
);
}
AppRegistry.registerComponent("AddClick", () => AddClick);
I have used this.props.maps as well as this.props.navigation which is showing an error:
this.props.navigation.navigate is undefined object
Trying to navigate to another page by rendering the firebase database but getting error but the same code i tried by simple creating a view and navigating to another page then it is working
export default class ItemComponent extends Component {
constructor(props) {
super(props);
// need to bind `this` to access props in handler
this._onEditLibrary = this._onEditLibrary.bind(this);
}
static propTypes = {
items: PropTypes.array.isRequired
};
_onEditLibrary=()=> {
this.props.navigation.navigate('EditLibrary');
};
render() {
return (
<View style={styles.itemsList}>
<TouchableOpacity onPress={this._onEditLibrary}>
{this.props.items.map((item, index) => {
return (
<View key={index}>
<ImageBackground source={item.Image} style={ { height:150, width:150}}>
<Text style={styles.itemtext}>{item.Name}</Text>
</ImageBackground>
</View>
)
})
}
</TouchableOpacity>
</View>
);
}
}
Need to navigate to another page
Try this out
export default class ItemComponent extends Component {
constructor(props) {
super(props);
// need to bind `this` to access props in handler
this._onEditLibrary = this._onEditLibrary.bind(this);
}
static propTypes = {
items: PropTypes.array.isRequired
};
_onEditLibrary=()=> {
this.props.navigation.navigate('EditLibrary');
};
render() {
return (
<View style={styles.itemsList}>
{this.props.items.map((item, index) => {
return (
<TouchableOpacity key={index} onPress={this._onEditLibrary}>
<ImageBackground source={item.Image} style={ { height:150, width:150}}>
<Text style={styles.itemtext}>{item.Name}</Text>
</ImageBackground>
</TouchableOpacity>
)
})
}
</View>
);
}
}
I've been using wix/react-native-navigation package to navigate between screens and handling the stack properly.
Moving across screens is pretty straightforward, firing those transitions when a button gets pressed. But the issue comes up when I have a FlatList and I want to push to a new screen when the user taps an item from the list, looks like the navigator props injected at the beginning is lost or in another context than the onPress callback event;
Here is the sample code
class AlertType extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.itemId, this.props.itemName, this.props.itemImageUrl);
};
render() {
return (
<TouchableOpacity { ...this.props }
onPress={ this._onPress }
style={ itemStyle.cardContainer }>
<View style={ itemStyle.mainContainer }>
<View style={{ width: 10 }}/>
<Image
source={{ uri: NET.HOST + this.props.itemImageUrl }}
style={{ width: 45, height: 45 }}
/>
<View style={{ width: 10 }}/>
<Text style={ itemStyle.itemText }>{ this.props.itemName }</Text>
</View>
</TouchableOpacity>
);
}
}
class AlertsScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
alertTypes: null,
}
}
_onAlertTypePressed(typeId: string, typeName: string, imageUrl: string){
this.props.navigator.push({
screen: 'prod.screens.AlertsCreator',
title: 'Alert',
passProps: {
alertId: typeId,
alertName: typeName,
alertImage: imageUrl
}
});
}
_renderListItem = ({ item }) => (
<AlertType
itemName={ item.titulo }
itemId={ item.key }
itemImageUrl={ item.url }
onPressItem={ this._onAlertTypePressed }
/>
);
render() {
return (
<View style={ styles.mainContainer }>
<FlatList
data={ this.state.alertTypes }
ItemSeparatorComponent={ () => <View style={{ height: 5 }}/> }
renderItem={ this._renderListItem }
/>
</View>
);
}
const mapSessionStateToProps = (state, ownProps) => {
return {
session: state.session
};
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(mapSessionStateToProps, mapDispatchToProps)(AlertsScreen)
This approach produces the next error
There have to be something I'm missing, I know this.props.navigator is not undefined, but inside on _onAlertTypePressed the navigator prop is undefined.
The problem is that you pass function to component without binding it to the current context.
You should pass:
this._onAlertTypePressed.bind(this);
another approach is binding your functions in the constructor:
constructor(props) {
this._onAlertTypePressed = this._onAlertTypePressed.bind(this);
}
I've had this happen before also.
I had to declare navigator between the render and return blocks
render() {
const navigator = this.props.navigator
return()}}
then pass navigator through when calling _onAlertTypePressed
() => _onAlertTypePressed(navigator)
then use navigator vs this.props.navigator inside _onAlertTypePressed
I'm trying to create a todo list, then I'm trying to use this checkbox package
react-native-check-box
I don't know if this issue come from that package
when I click the checkbox and click the delete button this is what happen
And here is my code for displaying the list
interface TodoProps{
todo: TodoModel;
ter:string;
}
interface TodoState{
dataSource: any;
myTodo: Array<ITodo>;
}
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => true});
export default class Todo extends React.Component <TodoProps,TodoState>{
constructor(props:TodoProps) {
super(props);
this.state = {
dataSource: ds.cloneWithRows([]), // or put your initial data here
myTodo: []
};
}
componentWillReceiveProps = (nextProps) => {
console.log(this.state.myTodo);
let data = {
title: nextProps.ter,
isChecked: false
};
let todoList = this.state.myTodo;
if (nextProps.ter) {
todoList.push(data);
}
this.setState({
myTodo: todoList
});
}
onDelete(){
let todos = this.state.myTodo.filter((todo:ITodo) => {
return !todo.isChecked;
});
this.setState({
myTodo: todos
});
}
render() {
let dataSource = this.state.dataSource.cloneWithRows(this.state.myTodo);
return (
<View style={styles.container}>
<View style={styles.box2}>
<ListView enableEmptySections={true} dataSource={dataSource} renderRow={(rowData, sectionID, rowID) => <TodoList data={rowData} onClick={this.onClick.bind(this)} id={rowID} /> } />
</View>
<View style={styles.box3}>
<TouchableOpacity style={styles.bbox1} onPress={()=> alert('weee')}>
<Text style={styles.tabText}>All</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bbox2} onPress={()=> alert('weee')}>
<Text style={styles.tabText}>Complete</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bbox3} onPress={()=> alert('weee')}>
<Text style={styles.tabText}>InComplete</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bbox4} onPress={()=> this.onDestroy()}>
<Text style={styles.tabText}>Delete</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
But if I console log the data. I find all isChecke: to false. Only the view of checkbox is not working. Do I need to use componentwillamount() for this?
Use the below sample code
_onRefresh() {
this.setState({refreshing: true});
// your callback function or call this.componentDidMount()
}
render() {
return (
<ListView
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}
...
>
...
</ListView>
);
}
I'm trying to add a TouchableHighlight component to a row in a list view.
The onPress function is throwing an undefined error in the code below. It works outside of the list view.
I suspect this is because I'm losing context of this but unsure how to fix. Anyone able to help?
export default class ConversationsList extends Component {
constructor(props) {
super(props);
this._handleChangePage = this._handleChangePage.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(Coversations.chats)
};
}
_handleChangePage(chat) {
this.props.navigator.push({
title: 'foo',
component: Chat,
passProps: {
chat: chat
}
});
}
renderRow(chat){
return (
<View>
<TouchableHighlight onPress={ () => this._handleChangePage.bind(this, chat) }>
<View>
/* more content removed */
</View>
</TouchableHighlight>
</View>
);
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
}
I also suspect that I shouldn't really be doing things this way, that my component should be structured differently, so it is passed the press handler as a prop perhaps. any advice appreciated.
You can declare another variable globally like
var _this;
initialise it in render method
render:function(){
_this = this;
return(
...
)
}
and use it in your touchableHightlight
renderRow(chat){
return (
<View>
<TouchableHighlight onPress={ () => _this._handleChangePage(chat) }>
<View>
/* more content removed */
</View>
</TouchableHighlight>
</View>
);
}
I suggest to read this helpful article
export default class ConversationsList extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(Coversations.chats)
};
}
_handleChangePage = () => {
this.props.navigator.push({
title: 'foo',
component: Chat,
passProps: {
chat: this
}
});
}
renderRow = (chat) => {
return (
<View>
<TouchableHighlight onPress={ this._handleChangePage }>
<View>
/* more content removed */
</View>
</TouchableHighlight>
</View>
);
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
}