Problem
I'm trying to use parameters I pass with react navigation in react native inside of functions. I define the parameters like this:
render() {
const { params } = this.props.navigation.state;
return (
And whenever I try to use a parameter in a function, for example params.userID I get an error saying, "can't find variable params". When I try to use
this.setState({passUserID: this.props.navigation.state.userID})
that just returns null. I would pass the parameters into the function, but I call functions from within other functions. The impractical solution I have been using is making a user press a button that defines a bunch of states with all of the parameters in them:
<Button
onPress={() =>{
this.setState({passKey: params.postKey})
this.setState({passUserID: params.userID})
this.setState({passContent: params.postContent})
this.setState({firebaseItems:''});
this.setState({fontLoaded:true});
}}
title="Press to load"
color="#ff0037"
/>
But this is impractical for the user, because each time they want to load this screen they have to press a button.
So how can I use params inside of functions, or what other solutions can I try?
Code
class Feed extends React.Component {
//...
//Where I send the parameters and navigate to the other screen
<TouchableWithoutFeedback onPress={() => {
this.props.navigation.navigate(
'CommentScreen',{postKey: item.key, postContent: item.content, passNameID: item.nameID, userID: item.userID}
)
}}>
//...
</TouchableWithoutFeedback>
//...
And then later:
class Comments extends React.Component {
//...
render() {
//where I receive the parameters
const { params } = this.props.navigation.state;
return (
//...
//How I use parameters
<Text>
{params && params.postContent}
</Text>
params is declared in render(). If you want to use the values in another function, you can use this.props.navigation.state.
Note: when you want to set several state values, you can do it all in one call to this.setState():
this.setState({
passKey: params.postKey,
passUserID: params.userID,
passContent: params.postContent,
firebaseItems:'',
fontLoaded:true
});
Related
Hi Guys I am a newbie in React-native I will like to know How I can called a function of one component when a button in another component is click like below I will like to call the onPlayPress() function from PlayWidget component when the button in AlbumHeader get click on
AlbumHeader.tsx file
export type AlbumHeaderProp = {
album: Album;
}
const AlbumHeader = (props: AlbumHeaderProp) => {
const { album } = props;
const playallSong = () => {
}
return (
<View style={styles.container}>
<TouchableOpacity onPress={playallSong}>
<View style={styles.button}>
<Text style={styles.buttonText} >
<PlayerWidget onPress={playallSong}/>
{Play}
</Text>
</View>
</TouchableOpacity>
</View>
)
}
export default AlbumHeader;
PlayWidget.tsx files
const onPlayPausePress = async () => {
if (!sound) {
return;
}
if (isPlaying) {
await sound.pauseAsync();
} else {
await sound.playAsync();
}
}
You could pass the function as prop to the component with the button. Then when you click the button, you can call that function
Component A - has function
Component B - has button
Assuming Component B is a child of Component A - then <Component B func={compAFunc}/>
Then in Component B button's onClick method
<btn onClick={props.func} />
If B is not a child of A, then you will probably have to lift state up to another component. Otherwise add the function to a global store like redux so it can be shared all over.
This is a good example of a use case for an observer pattern.
In the observer pattern there is a particular event that an "observer" cares about in the "publisher" (There are other names used for these things).
To sum it up, the "observer" (sometimes called "Subscriber") is the one who needs to be called when an event occurs. The "Publisher" or "Observable" is the one who will do the calling of all subscribers when a particular event occurs.
The "publisher" will have an "on" or "subscribe" function that registers the observer to be called for the particular event.
Here is a great post for more about the Observer Pattern
Refactoring Guru - Observer
How does it help you?
your code will look something like the following...
interface AlbumHeaderProps {
// ...
onPlayerWidgetPress(): Promise<void> // analogous to .subscribe(Observer)
}
const AlbumHeader = ({
onPlayerWidgetPress,
}: AlbumHeaderProps) => {
return (
<View style={styles.container}>
// ...
<PlayerWidget
onPress={onPlayerWidgetPress}
/>
// ...
</View>
)
}
Then in the code that calls this component...
<AlbumHeader
onPlayerWidgetPress={async () => { // analogous to .update() method on observer
//... your function
}}
/>
NOTE: I don't know how or if PlayerWidget honors/awaits promises so I can't speak to concurrency control here.
Your final solution may vary, but hopefully this helps to get you part of the way there.
I have a child component "Text Input" and passes the value to as a prop like this
export default function MobileInput(props) {
const [mobileNumber, setMobileNumber] = React.useState('');
return (
<View style={styles.inputBox}>
<TextInput
value={mobileNumber}
onChangeText={(number) => setMobileNumber(number)}
onEndEditing={props.saveMobileNumber(mobileNumber)} // here
/>
</View>
);
}
In Parent, I got the value from child
const [mobile, setMobile] = useState('');
const getMobile = (number) => {
number ? setMobile(number) : null; // here's I got this warnning
console.log('getMobile-number-from-child', number);
};
const reSendMobile = () => { // other function I want to call passed on mobile number I got from child component
if (mobile?.length) {
alert('Done');
setReSend(false);
setSeconds(10);
} else {
alert('Please write your number before press send!');
}
};
<MobileInput saveMobileNumber={getMobile} />
I see this issue But I'm already using React 16.13.1
TextInputs property onEndEditing accepts a function that is called when text input ends.. Instead of a function, you are passing the result of your props.saveMobileNumber function that is called when the component renders. Try passing a function that calls saveMobileNumber instead:
onEndEditing={() => props.saveMobileNumber(mobileNumber)}
Your code will be much easier to read/debug if you avoid keeping the same state in multiple components. You can pass mobile and setMobile to the child through props and avoid having to create separate state for the same data.
Try this:
<View style={styles.inputBox}>
<TextInput
value={mobileNumber}
onChangeText={(number) => setMobileNumber(number)}
onEndEditing={() => props.saveMobileNumber(mobileNumber)} // change here
/>
</View>
The event onEndEditing accepts a function call
Just update to call a arrow function :
onEndEditing={() => props.saveMobileNumber(mobileNumber)}
For me, i was updating activity title outside the useEffect hook. When i moved the code
into useEffect hook, the error just gone.
I am trying to call a custom function inside a custom button in my react navigation header. I've looked around several ways to do this, and the best result I've found is making the function static, that is:
export class MyClass extends React.Component{
static navigationOptions = ({navigation}) => ({
headerRight: (<Button title='press me' onPress={()=> MyClass.SomeFunction() } ></Button>)
});
static SomeFunction(){
/*Some code here*/
}
/*Some extra code here*/
}
My issue is, however, that I need to access some state properties within SomeFunction() and, as you may know, you cannot acces this within a static function.
Is there any way I can access the state of a component within a static, or is there a better way to implement a custom function within a button in the header????
As an alternative solution you might set the navigator state to set and get values.
If you use an AppWithNavigation state parent as a root of your navigation structure you should be pass a navigation prop to children elements like below:
render() {
const { dispatch, nav, } = this.props
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: dispatch,
state: nav,
})}
/>
)
}
If so, just set your values by using the following line:
this.props.navigation.setParams({someValue: 'Value'})
Then get your set value whenever you want like the below:
this.props.navigation.state.someValue
Or
const { someValue } = this.props.navigation.state
But keep in mind, when first rendering the component state may be null or undefined. So you need to check its existing before try to get:
if (!this.props.navigation.state) {
return null
}
const someValue = this.navigation.state.someValue
if (someValue) {
/* you can use your someValue here! */
}
Note to that every route has its own state object. When your screen is changed, the state of your this.props.navigation.state object is changed. If you need a global solution, I think, you might use Redux.
after some time messing around with the code, I found a solution that better fits my needs. I post it below in case it helps anyone. Thank you all for your contributions :D
export class MyClass extends React.Component{
static navigationOption = ({navigation}) => ({
headerRight: (<Button title='Press Me!' onPress={() => MyClass.SomeFunc() })
})
//The function
static SomeFun(){
alert(MyClass.SomeState.abc)
}
//Static functioning as state
static SomeState = {
abc: 'def'
}
}
Here is an approach straight from their documentation https://reactnavigation.org/docs/en/header-buttons.html
There is also an npm module to make this a bit easier. https://www.npmjs.com/package/react-navigation-underscore
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const params = navigation.state.params || {};
return {
headerTitle: <LogoTitle />,
headerRight: (
<Button onPress={params.increaseCount} title="+1" color="#fff" />
),
};
};
componentWillMount() {
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 */
}
This may be more a javascript question than a react-native/meteor question: I am adding Meteor connectivity to an existing React Native app, and have run into a snag with navigation. I previously had a ListView that provided an onPress function each row that would call the navigation. In keeping with Meteor's createContainer protocol, I've used (in my case) a "PuzzlesContainer" in place of the ListView that, in a separate file, refers to
const PuzzlesContainer = ({ puzzlesReady }) => {
return (
<Puzzles
puzzlesReady={puzzlesReady}
/>
);
};
export default createContainer(() => {
const handle = Meteor.subscribe('puzzles-list');
return {
puzzlesReady: handle.ready(),
};
}, PuzzlesContainer);
This file includes the "Puzzles" file, which is also a const function that contains the MeteorListView:
const Puzzles = ({ puzzlesReady }) => {
if (!puzzlesReady) {
return null;//<Loading />;
}else{
return (
<View style={launcherStyle.container}>
<MeteorListView
collection="puzzles"
renderRow={
(puzzle) =>
<View >
<TouchableHighlight style={launcherStyle.launcher} onPress={()=>onSelect(puzzle.text)}>
<Text style={launcherStyle.text}>{puzzle.text}</Text>
</TouchableHighlight>
</View>
. . .
My problem is that there is now no context for the original routing scheme, so when I call
this.props.navigator.push
it gives "undefined is not an object (evaluating 'this.props.navigator')". How can I handle this?
One way is to look at the new NavigationExperimental, which handles nagivator in a redux fashion.
Another method is, even though I do not know if this is recommended or not, to globalize the navigator component by assigning it to a module. It can be something like this
// nav.js
let nav = null
export function setNav = _nav => nav = _nav
export function getNav = () => {
if (nav) {
return nav
} else {
throw "Nav not initialized error"
}
}
Then when you first get hold of your navigator, do this
// component.js
import { Navigator } from 'react-native'
import { setNav } from './nav'
// ...
renderScene={ (route, navigator) => {
setNav(navigator)
// render scene below
// ...
}}
As much as I liked the suggestion of globalizing my navigation, a) I never managed to do it and b) it seemed like maybe not the best practice. For anyone else who might encounter this issue, I finally succeeded by passing the navigation props in each of the JSX tags--so:
<PuzzlesContainer
navigator={this.props.navigator}
id={'puzzle contents'}
/>
in the parent (react component) file, then
<Puzzles
puzzlesReady={puzzlesReady}
navigator={navigator}
id={'puzzle contents'}
/>
in the second 'const' (Meteor container) file, and using it
<TouchableHighlight onPress={()=>navigator.replace({id: 'puzzle launcher', ... })}>
in the third 'const' (MeteorListView) file. Hope it helps someone!
I have a list of tag objects in array (tagList), which I am fetching from the server and storing into the state. After a succesfull fetch I am mapping each item inside a ScrollView.
export default class RegisterTags extends Component {
constructor(props) {
super(props)
this.state = {
tagList: [],
selectedTags: [],
}
}
componentWillMount() {
api.getTagsOnRegister()
.then((res) => {
this.setState({
tagList: res
})
})
}
insertTag = (tag) =>{
this.setState({
selectedTags: this.state.selectedTags.push(tag)
})
}
render() {
return(
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.ScrollViewContainer}>
{this.state.tagList.map((tag) => {
return(
<View style={styles.tagStyle} key={tag.id} onPress={this.insertTag(tag)}>
<Text style={styles.tagText}>{tag.name}</Text>
</View>
)
})}
</ScrollView>
</View>
)
}
}
What I want to achieve is, when I press on any of the tag, I would like to add that object into the selectedTags array. But I am getting error:
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
Possible Unhandled Promise Rejection (id: 0):
_this.state.selectedTags.push is not a function
TypeError: _this.state.selectedTags.push is not a function
How can I add the pressed tag item into the selectedTags array?
onPress={this.insertTag(tag)}
Issue: in your render function you are directly calling a function that changes state. Render function should never call any function that updates state.
So how to call onPress?
Ans: as written below
onPress = {() => {this.insertTag(tag) }}
Will code work now?
Answer is no.
You have to put your views inside TouchableHighlight and move onPress method from View to TouchableHighlight.
Then hopefully your code works. I am assuming everything is setup property.
~~~~~~EDIT 1~~~~~~~~
What't the difference between onPress={this.insertTag(tag)} and onPress = {() => {this.insertTag(tag) }}
Every thing inside curly braces are expressions in react jsx. Now onPress={this.insertTag(tag)} evaluates the expression inside curly braces and assign it to onPress property, and in your case this.insertTag happens to update state.
While onPress = {() => {this.insertTag(tag) }} , on evaluating curly braces returns a function, but doesn't call that function. And when onPress event is triggered than that function is called.
Google "Arrow Function" for more.