10

I need to call SearchScreen class method from a React Navigation Header.

The Navigator look like this:

  Search: {
    screen: SearchScreen,
    path: 'search/:query',
    navigationOptions: {
      title: 'Search',
      header: {
        right: (
          <MaterialCommunityIcons
            name="filter"
            onPress={() => {
              console.log(this);
            }}
            style={{marginRight: 15, color: 'white'}}
            size={24}
          />
        ),
      },
    }
  }
1
  • The only way I've added functionality was to add the logic in the button or Icon component itself and use a redux action - rather than referencing the screen itself Commented Feb 22, 2017 at 18:14

5 Answers 5

13

I've made it work by doing:

// declare static navigationOptions in the Component
static navigationOptions = {
  title: 'Title',
  header: ({ state }) => ({
    right: (
      <MaterialCommunityIcons
        name="filter"
        onPress={state.params.handleFilterPress}
        style={{marginRight: 15, color: 'white'}}
        size={24}
      />
    ),
  }),
}

_handleFilterPress() {
  // do something
}


componentDidMount() {
  // set handler method with setParams
  this.props.navigation.setParams({ 
    handleFilterPress: this._handleFilterPress.bind(this) 
  });
}

Sign up to request clarification or add additional context in comments.

3 Comments

@KartiikeyaBaleneni try navigation.setParams on componentWillMount, does it throws any error?
This pattern worked for me and I created a simple HOC to make this a little easier to implement as well -- npmjs.com/package/react-navigation-underscore
You also have set params in your navigator otherwise it will be undefined @Kartiikeya
2

I've resolved the issue the following way:

static navigationOptions = ({ navigation }) => {
    return {
        headerRight: () => (

            <TouchableOpacity
                onPress={navigation.getParam('onPressSyncButton')}>
                <Text>Sync</Text>
            </TouchableOpacity>
        ),
    };
};

componentDidMount() {
    this.props.navigation.setParams({ onPressSyncButton: this._onPressSyncButton });
}

_onPressSyncButton = () => {
     console.log("function called");
}

Comments

1

Hooks solution with FunctionComponent, useState and useEffect

Ref the official docs (https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component) it is done by:

class HomeScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    return {
      headerTitle: <LogoTitle />,
      headerRight: (
        <Button
          onPress={navigation.getParam('increaseCount')}
          title="+1"
          color="#fff"
        />
      ),
    };
  };

  componentDidMount() {
    this.props.navigation.setParams({ increaseCount: this._increaseCount });
  }

  state = {
    count: 0,
  };

  _increaseCount = () => {
    this.setState({ count: this.state.count + 1 });
  };

  /* later in the render function we display the count */
}

However I could not get this to work when working with the hooks api. My state variables were always undefined, but after I thought about how the hooks api is implemented it all made sense, so the solution was to update the navigation param every time a significant state variable was changed:

const [count, setCount] = useState(0);

useEffect(() => {
    props.navigation.setParams({ increaseCount });
}, [count]);

const increaseCount = () => setCount(count + 1);

2 Comments

I get an infinite loop when trying to setParams with useEffect. Can you share your complete code example?
@Jason I think maybe this can happen if you have other state changing functionality in the useEffect function. The way I fixed it was by checking if it needs to be updated and update accordingly. For instance I have a User object which is loaded in useEffect and this is only loaded if the object is undefined and then continue with updating props.navigation.setParams({ increaseCount }); Sorry about the delay, and please let me know if you still want me to change the answer to my specific version.
0

I came across same issue and able to resolve the issue from below links.

class MyScreen extends React.Component {
    static navigationOptions = {
        header: {
            right: <Button title={"Save"} onPress={() => this.saveDetails()} />
        }
    };

    saveDetails() {
        alert('Save Details');
    }

    render() {
        return (
            <View />
        );
    }
}

Source: react-native issues 145

Below is my code

import React, { Component } from "react";
import {
  Container,
  Header,
  Item,
  Input,
  Icon,
  Button,
  Text,
  Left,
  Body,
  Right,
  Content,
  Spinner,
  List,
  ListItem
} from "native-base";
import { View, Image, StyleSheet, Keyboard } from "react-native";
import { connect } from "react-redux";
import {
  onClear,
  onSearchTextChanged,
  searchForProfiles
} from "../../actions/searchActions";

class SearchBar extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <Header searchBar rounded>
        <Button
          iconLeft
          style={{ paddingLeft: 0 }}
          light
          onPress={this.props.onBackPress}
        >
          <Icon style={{ marginLeft: 0, fontSize: 35 }} name="arrow-back" />
        </Button>
        <Item>
          <Icon name="ios-search" />
          <Input
            placeholder="Search"
            onChangeText={this.props.onChangeText}
            value={this.props.searchText}
          />
          <Button small transparent onPress={this.props.onClear}>
            <Icon name="ios-close" />
          </Button>
        </Item>
        <Button transparent onPress={this.props.onSearch}>
          <Text>Search</Text>
        </Button>
      </Header>
    );
  }
}

class SearchWorld extends Component {
  static navigationOptions = ({ navigation }) => ({
    left: null,
    header: () => {
      const { state } = navigation;
      return (
        <SearchBar
          onBackPress={() => navigation.goBack()}
          onChangeText={text => {
            state.params.onChangeText(text);
          }}
          onSearch={state.params.onSearch}
          onClear={state.params.onClear}
          searchText={state.params.searchText}
        />
      );
    }
  });

  onChangeText = text => {
    this.props.navigation.setParams({
      ...this.props.navigation.state,
      searchText: text
    });
    this.props.onSearchTextChanged(text);
  };

  onSearch = () => {
    Keyboard.dismiss();
    const profile = { search: "test" };
    const token = this.props.token;
    this.props.searchForProfiles(token, profile);
  };

  onClear = () => {
    this.props.onClear();
    this.props.navigation.setParams({
      ...this.props.navigation.state,
      searchText: ""
    });
  };

  componentDidMount() {
    this.props.navigation.setParams({
      onChangeText: this.onChangeText,
      onSearch: this.onSearch,
      onClear: this.onClear,
      searchText: this.props.searchText
    });
  }

  render() {
    const { searchResults } = this.props;
    let items = [];
    if(searchResults && searchResults.data && searchResults.data.length > 0) {
      items = [...searchResults.data];
    }
    return this.props.loading ? (
      <Container style={{ alignItems: "center", justifyContent: "center" }}>
        <Spinner color="#FE6320" />
      </Container>
    ) : (
      <Container>
        <Content>
          <List
            style={{}}
            dataArray={items}
            renderRow={item => (
              <ListItem style={{ marginLeft: 0}}>
                <Text style={{paddingLeft: 10}}>{item.password}</Text>
              </ListItem>
            )}
          />
        </Content>
      </Container>
    );
  }
}

const mapStateToProps = state => {
  const { token } = state.user;
  const { searchText, searchResults, error, loading } = state.searchReaducer;

  return {
    token,
    searchText,
    searchResults,
    error,
    loading
  };
};

export default connect(mapStateToProps, {
  onClear,
  onSearchTextChanged,
  searchForProfiles
})(SearchWorld);

Comments

0
static navigationOptions =  ({navigation}) => {
return {
 headerTitle: () => <HeaderTitle />,
 headerRight: () => (<Button iconLeft transparent small onPress = {navigation.getParam('onPressSyncButton')}>
      <Icon style ={{color:'white', fontWeight:'bold'}} name='md-save' size = {32} />
           <Text style ={{color:'white', fontWeight:'bold'}}>save</Text>
    </Button>),
 headerTintColor:'black',
 headerStyle: {
   backgroundColor: '#6200EE'
 },
}
};
this.props.navigation.setParams({ onPressSyncButton: this.updateUserProfile });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.