Can't find variable: navigate - javascript

I am trying to navigate between two screen with the help of react-navigation. I am able to access navigate inside the render method as its scope is also inside that method.
Where should I declare so I can access it any method of this component. I am trying to access navigate inside the onPressButton method but it giving an error.
Can't find variable: navigate
import React, { Component } from "react";
import { View, Text, Image, Button, Alert, StyleSheet } from "react-native";
import styles from "./Styles";
import * as strings from "./Strings";
import RoundButton from "./RoundButton";
var DialogAndroid = require("react-native-dialogs");
import { StackNavigator } from "react-navigation";
export default class CreateMessageScreen extends Component {
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Image source={require("./img/create_message.png")} />
<Text style={styles.textStyle}>{strings.create_message}</Text>
<RoundButton
textStyle={styles.roundTextStyle}
buttonStyle={styles.roundButtonStyle}
onPress={this.onPressButton}
>
CREATE MESSAGE
</RoundButton>
</View>
);
}
onPressButton() {
var options = {
title: strings.app_name,
content: strings.create_message,
positiveText: strings.OK,
onPositive: () => navigate("DashboardScreen")
};
var dialog = new DialogAndroid();
dialog.set(options);
dialog.show();
}
}

You need to move const { navigate } = this.props.navigation; into the onPressButton function instead of the render function (don't forget to bind the function so that this has the correct value):
export default class CreateMessageScreen extends Component {
constructor() {
super();
// need to bind `this` to access props in handler
this.onPressButton = this.onPressButton.bind(this);
}
render() {
return (
<View style={styles.container}>
<Image source={require("./img/create_message.png")} />
<Text style={styles.textStyle}>{strings.create_message}</Text>
<RoundButton
textStyle={styles.roundTextStyle}
buttonStyle={styles.roundButtonStyle}
onPress={this.onPressButton}
>
CREATE MESSAGE
</RoundButton>
</View>
);
}
onPressButton() {
const { navigate } = this.props.navigation;
var options = {
title: strings.app_name,
content: strings.create_message,
positiveText: strings.OK,
onPositive: () => navigate("DashboardScreen")
};
var dialog = new DialogAndroid();
dialog.set(options);
dialog.show();
}
}

Object destructuring work like this,
Destructuring objects:
const obj = { first: 'Jane', last: 'Doe' };
const {first: f, last: l} = obj;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const {first, last} = obj;
// first = 'Jane'; last = 'Doe'
In Your Case:
1. const { navigation:navigate } = this.props;
or:
2. const {navigation} = this.props;
export default class CreateMessageScreen extends Component {
render() {
const { navigation:navigate } = this.props;
return (
<View style={styles.container}>
<Image source={require("./img/create_message.png")} />
<Text style={styles.textStyle}>{strings.create_message}</Text>
<RoundButton
textStyle={styles.roundTextStyle}
buttonStyle={styles.roundButtonStyle}
onPress={this.onPressButton}
>
CREATE MESSAGE
</RoundButton>
</View>
);
}
onPressButton() {
const { navigation:navigate } = this.props;
var options = {
title: strings.app_name,
content: strings.create_message,
positiveText: strings.OK,
onPositive: () => navigate("DashboardScreen")
};
var dialog = new DialogAndroid();
dialog.set(options);
dialog.show();
}
}

That happens because you are not destructuring it from the props as you have done in your render() function
onPressButton = () => {
var {navigate} = this.props.navigation;
var options = {
title: strings.app_name,
content: strings.create_message,
positiveText: strings.OK,
onPositive: () => navigate("DashboardScreen")
};
var dialog = new DialogAndroid();
dialog.set(options);
dialog.show();
}

For those looking for the hooks version in react-navigation v6 👇🏼
import {useNavigation} from '#react-navigation/native
then inside the components call the hook
const someFunction = () => {
const navigate = useNavigation() 👈🏼
}

Related

How to export function from class components? REACT-NATIVE

I have a class component and I need to pass one of its function to the drawer and use it, but don't know if it's possible:
class Edit_note extends Component {
save_changes = async() => {
let clear_content = this.state.content.replace(/ /g,""); //replace al
try {
const data = JSON.parse(await AsyncStorage.getItem("data"));
const index_to_find = this.array_notes.findIndex(obj => obj.note_number === this.note[0].note_number);
const edited_note = this.note.map((note) => {
note.content = clear_content;
return {...note}
});
this.array_notes.splice(index_to_find, 1, edited_note[0]);
data.array_notes = this.array_notes;
await AsyncStorage.setItem("data", JSON.stringify(data));
} catch(error) {
alert(error);
}
}
render() {
return (
<>
<Text>hi</Text>
</>
);
}
}
export default Edit_note;
this is my class component, and I want to export the function save_changes and use it in the header of edit note, put it in the icon
import React, { Component } from "react";
import { Text, View } from "react-native";
import { Feather } from '#expo/vector-icons';
class Header extends Component {
render() {
return (
<>
<View style ={{backgroundColor: "white", flexDirection: "row", alignItems: "center"}}>
<View>
<Text style = {{color: "black", fontSize: 30, marginLeft: -20}}>{this.props.title}</Text>
</View>
<Feather name="check-square" size={24} color = "black" />
</View>
</>
);
}
}
export default Header;
How can I do that?
You can not do that. Only way is to extract save_changes out of the class as a utility, export it and use it where ever u need passing the right parameters.
In the file where you have your class component, make the following changes:
export const save_changes = async(<Pass the parameter here>) => {
let clear_content = this.state.content.replace(/ /g,""); //replace al
try {
const data = JSON.parse(await AsyncStorage.getItem("data"));
const index_to_find = this.array_notes.findIndex(obj => obj.note_number === this.note[0].note_number);
const edited_note = this.note.map((note) => {
note.content = clear_content;
return {...note}
});
this.array_notes.splice(index_to_find, 1, edited_note[0]);
data.array_notes = this.array_notes;
await AsyncStorage.setItem("data", JSON.stringify(data));
}catch(error) {
alert(error);
}
}
class Edit_note extends Component {
render() {
return (
<>
<Text>hi</Text>
</>
);
}
}
export default Edit_note;
Now you can import the save_changes function anywhere from this file
To export a function from your component, you need it to be public static, and remove all usage of this within the function. The public word means that the function can be accessed from outside the component, with Edit_note.save_changes(). The keyword static is used to make the function independant of the component's instance.
To import the data you need from your instance, you can pass them as parameters in your function save_changes.
As a result, you should have something like this:
public static async save_changes (content, array_notes, note) {
let clear_content = content.replace(/ /g,""); //replace al
try {
const data = JSON.parse(await AsyncStorage.getItem("data"));
const index_to_find = array_notes.findIndex(obj => obj.note_number === note[0].note_number);
const edited_note = note.map((note) => {
note.content = clear_content;
return {...note}
});
array_notes.splice(index_to_find, 1, edited_note[0]);
data.array_notes = array_notes;
await AsyncStorage.setItem("data", JSON.stringify(data));
} catch(error) {
alert(error);
}
}
And then, you can call this function in any other component with Edit_note.save_changes(), as long as the component Edit-note is imported as well at the top lines of the component.

Redux store isn't getting updated

I have built this app using create-react-native-app, the action is dispatched but the state isn't being updated and I'm not sure why.
I see the action being logged (using middleware logger) but the store isn't getting updated, I am working on Add_Deck only for now
Here is my reducer:
// import
import { ADD_CARD, ADD_DECK } from './actions'
// reducer
export default function decks(state ={}, action){
switch(action.type){
case ADD_DECK:
return {
...state,
[action.newDeck.id]: action.newDeck
}
case ADD_CARD:
return {
...state,
[action.deckID]: {
...state[action.deckID],
cards: state[action.deckID].cards.concat([action.newCard])
}
}
default: return state
}
}
Actions file:
// action types
const ADD_DECK = "ADD_DECK";
const ADD_CARD = "ADD_CARD";
// generate ID function
function generateID() {
return (
"_" +
Math.random()
.toString(36)
.substr(2, 9)
);
}
// action creators
function addDeck(newDeck) {
return {
type: ADD_DECK,
newDeck
};
}
// export
export function handleAddDeck(title) {
return dispatch => {
const deckID = generateID();
// const newDeck = { id: deckID, title, cards: [] };
dispatch(addDeck({ id: deckID, title, cards: [] }));
};
}
function addCard(deckID, newCard) {
// { question, answer }, deckID
return {
type: ADD_CARD,
deckID,
newCard
};
}
// export
export function handleAddCard(deckID, content) {
// { question, answer }, deckID
return dispatch => {
const newCard = { [generateID()]: content };
dispatch(addCard(deckID, newCard));
};
}
And react-native component:
import React, { Component } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity } from "react-native";
import {red, white} from '../utils/colors'
import { connect } from 'react-redux'
import { handleAddDeck } from '../redux/actions'
class AddDeck extends Component {
state = {
text:""
}
handleSubmit = () => {
this.props.dispatch(handleAddDeck(this.state.text))
this.setState(()=>{
return { text: ""}
})
}
render() {
return (
<View style={styles.adddeck}>
<Text> This is add deck</Text>
<TextInput
label="Title"
style={{ height: 40, borderColor: "gray", borderWidth: 1 }}
onChangeText={text => this.setState({ text })}
placeholder="Deck Title"
value={this.state.text}
/>
<TouchableOpacity style={styles.submitButton} onPress={this.handleSubmit}>
<Text style={styles.submitButtonText}>Create Deck</Text>
</TouchableOpacity>
</View>
);
}
}
function mapStateToProps(decks){
console.log("state . decks", decks)
return {
decks
}
}
export default connect(mapStateToProps)(AddDeck);
const styles = StyleSheet.create({
adddeck: {
marginTop: 50,
flex: 1
},
submitButton: {
backgroundColor: red,
padding: 10,
margin: 15,
height: 40,
},
submitButtonText: {
color: white
}
});
I guess you forgot to export your types from the actions file thus the switch(action.type) does not trigger the needed case statement.
Maybe try to add as the following:
export const ADD_DECK = "ADD_DECK";
export const ADD_CARD = "ADD_CARD";
Or further debugging just to see if the values are the ones what you are looking for:
export default function decks(state = {}, action) {
console.log({type:action.type, ADD_DECK}); // see what values the app has
// further code ...
}
I hope that helps! If not, let me know so we can troubleshoot further.

Why can't I add any value to the array in state?

I have a lot of hits, which I want to add to an array once a hit is pressed. However, as far as I observed, the array looked like it got the name of the hit, which is the value. The value was gone in like half second.
I have tried the methods like building constructor, and doing things like
onClick={e => this.handleSelect(e)}
value={hit.name}
onClick={this.handleSelect.bind(this)}
value={hit.name}
onClick={this.handleSelect.bind(this)}
defaultValue={hit.name}
and so on
export default class Tagsearch extends Component {
constructor(props) {
super(props);
this.state = {
dropDownOpen:false,
text:"",
tags:[]
};
this.handleRemoveItem = this.handleRemoveItem.bind(this);
this.handleSelect = this.handleSelect.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
}
handleSelect = (e) => {
this.setState(
{ tags:[...this.state.tags, e.target.value]
});
}
render() {
const HitComponent = ({ hit }) => {
return (
<div className="infos">
<button
className="d-inline-flex p-2"
onClick={e => this.handleSelect(e)}
value={hit.name}
>
<Highlight attribute="name" hit={hit} />
</button>
</div>
);
}
const MyHits = connectHits(({ hits }) => {
const hs = hits.map(hit => <HitComponent key={hit.objectID} hit={hit}/>);
return <div id="hits">{hs}</div>;
})
return (
<InstantSearch
appId="JZR96HCCHL"
apiKey="b6fb26478563473aa77c0930824eb913"
indexName="tags"
>
<CustomSearchBox />
{result}
</InstantSearch>
)
}
}
Basically, what I want is to pass the name of the hit component to handleSelect method once the corresponding button is pressed.
You can simply pass the hit.name value into the arrow function.
Full working code example (simple paste into codesandbox.io):
import React from "react";
import ReactDOM from "react-dom";
const HitComponent = ({ hit, handleSelect }) => {
return <button onClick={() => handleSelect(hit)}>{hit.name}</button>;
};
class Tagsearch extends React.Component {
constructor(props) {
super(props);
this.state = {
tags: []
};
}
handleSelect = value => {
this.setState(prevState => {
return { tags: [...prevState.tags, value] };
});
};
render() {
const hitList = this.props.hitList;
return hitList.map(hit => (
<HitComponent key={hit.id} hit={hit} handleSelect={this.handleSelect} />
));
}
}
function App() {
return (
<div className="App">
<Tagsearch
hitList={[
{ id: 1, name: "First" },
{ id: 2, name: "Second" },
{ id: 3, name: "Third" }
]}
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
additionally:
note the use of prevState! This is a best practice when modifying state. You can google as to why!
you should define the HitComponent component outside of the render method. it doesn't need to be redefined each time the component is rendered!

How to Define Child Navigator's Initial Route Name from Parent Navigator?

I managed to pass initial route name from parent navigator to child navigator, but Log Out button in Drawer is not working (do nothing, no errors). Is it because I created multiple AppContainers?
NavApp.js
const routeConfigs = {
NavGuest: { screen: NavGuest },
NavDrawer: { screen: NavDrawer }
}
const AppContainerIn = (props) => {
navConfigs.initialRouteName = props.initialRouteName;
let switchNav = createSwitchNavigator(routeConfigs, navConfigs);
let AppContainerOut = createAppContainer(switchNav);
return <AppContainerOut />
}
export default class NavApp extends Component {
render() {
return (
<AppContainerIn initialRouteName={this.props.initialRouteName} />
)
}
}
NavDrawer.js
const routeConfigs = {
Wizard: { screen: Wizard },
NavHomeSearch: { screen: NavHomeSearch },
}
const navConfigs = {
contentComponent: SideMenu,
drawerWidth: Dimensions.get('window').width - 120,
}
const ContainerDrawerIn = (props) => {
navConfigs.initialRouteName = props.initialRouteName;
let NavDrawer = createDrawerNavigator(routeConfigs, navConfigs);
let ContainerDrawerOut = createAppContainer(NavDrawer);
return <ContainerDrawerOut />
}
export default class ContainerDrawer extends Component {
render() {
return (
<ContainerDrawerIn initialRouteName={this.initialRouteName} />
)
}
}
SideMenu.js
export default class SideMenu extends Component {
constructor(props) {
super(props);
this.navigation = props.navigation;
}
logout = () => {
AsyncStorage.removeItem('isLoggedin');
// Help, not going anywhere. Btw, isLoggedin is successfully removed.
this.props.navigation.navigate('NavGuest');
}
render() {
return (
<View>
<Button title='Log Out' onPress={() => this.logout()} />
</View>
)
}
}
Common mistake: Explicitly rendering more than one navigator.
https://reactnavigation.org/docs/en/common-mistakes.html
export default class App extends React.Component {
render() {
/* In the root component we are rendering the app navigator */
return <AppContainer />;
}
}
const AuthenticationNavigator = createStackNavigator({
SignIn: SignInScreen,
ForgotPassword: ForgotPasswordScreen,
});
class AuthenticationScreen extends React.Component {
static router = AuthenticationNavigator.router;
render() {
return (
<AuthenticationNavigator navigation={this.props.navigation} />
);
}
}
const AppNavigator = createSwitchNavigator({
Auth: AuthenticationScreen, // This screen renders a navigator!
Home: HomeScreen,
});
const AppContainer = createAppContainer(AppNavigator);

React Native - Edit other component's state

I want to edit other componenet's state by running function which checks if the current state of the app is active, Which means it runs each time the app come to the forground.
let d = new Date().getHours();
let title = messages[`_${d}`].message;
function getAppState() {
AppState.addEventListener("change", (state) => {
if(state == "active") {
d = new Date().getHours();
title = messages[`_${d}`].message;
// App.setState({ text: title }); **Not working!
console.log(title);
}
});
}
export default class App extends React.Component {
constructor() {
super();
this.state = { text: title };
getAppState();
}
render() {
return (
<View>
<StatusBar hidden={ true } />
<Text style={styles.title}>{title}</Text>
</View>
);
}
}
I want to change text's value according to the hour.
I don't have an environment to test this but I would do it like this:
export default class App extends React.Component {
constructor() {
super();
this.state = {
text: title
};
// Bind updateTile() so `this` corresponds tho the react component and things
// like `this.setState()` are available. Otherwise `this` would be `AppState`
this.updateTitle = this.updateHour.bind(this);
}
componentWillMount() {
// Listen for the event
AppState.addEventListener("change", this.updateTitle);
}
updateTitle(state) {
if (state == "active") {
const d = new Date().getHours();
const title = messages[`_${d}`].message;
this.setState({
text: title
});
console.log(title);
}
}
render() {
return (
<View >
<StatusBar hidden={true} />
<Text style = {styles.title}>{title}</Text>
</View >
);
}
}
If you wanted updateTitle() to be another function and is not part of the Component I would do this:
const updateComponentTitle = state => {
if (state == "active") {
const d = new Date().getHours();
const title = messages[`_${d}`].message;
this.setState({
text: title
});
console.log(title);
}
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
text: title
};
// Bind updateTile() so `this` corresponds tho the react component and things
// like `this.setState()` are available. Otherwise `this` would be `AppState`
this.updateTitle = udpateComponentTitle.bind(this);
}
componentWillMount() {
// Listen for the event
AppState.addEventListener("change", this.updateTitle);
}
render() {
return (
<View >
<StatusBar hidden={true} />
<Text style = {styles.title}>{title}</Text>
</View >
);
}
}

Categories

Resources