use componentWillMount without a class - javascript

with react-native, I want to use componentWillMount without using a class
await Font.loadAsync({
gotham_medium: require("../../assets/GothamMedium_1.ttf")
});
}
const Button = (props: TouchableOpacityProps & ButtonProps) => (
<TouchableOpacity {...props} style={styles.button}>
<Text style={styles.title}>{props.title}</Text>
</TouchableOpacity>
);
export default Button;
But I have a problem on the device :
error on the device

It says the problem is on this line (and it is):
async componentWillMount = () => {
When you use an async function, the async keyword goes right before () => (a vanilla js syntax error). Like this:
componentWillMount = async () => {
But, that's not the main problem. When not using a class, you need the useEffect hook.
So, try something like this (the whole component, and deleting componentWillMount):
const Button = (props: TouchableOpacityProps & ButtonProps) => {
useEffect(async () => {
await Font.loadAsync({
gotham_medium: require("../../assets/GothamMedium_1.ttf")
});
}, []);
return (
<TouchableOpacity {...props} style={styles.button}>
<Text style={styles.title}>{props.title}</Text>
</TouchableOpacity>
);
};
And at the top of the file:
import { useEffect } from 'react';

You can use Hooks for this,
from the docs,
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
And
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
useEffect(async () => {
await Font.loadAsync({
gotham_medium: require("../../assets/GothamMedium_1.ttf")
});
},[]);

Related

How can I call a function from one component when a button in another component is clicked

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.

cannot update a component from inside the function body of a different component - React Native?

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.

React native setState hook reopens modal

I have a weird issue with a modal in react native (expo). My modal looks like this:
const useNewCommentModal = () => {
const [showModal, setShowModal] = useState(false);
const [comment, setComment] = useState('');
const toggle = () => {
setShowModal(!showModal);
};
const NewCommentModal = () => (
<Modal visible={showModal} animationType="slide">
<View style={[t.pX4]}>
<TextInput
style={[t.bgWhite, t.p2, t.rounded, t.textLg]}
placeholder={'Post jouw reactie...'}
onChangeText={text => setComment(text)}
value={comment}
/>
</View>
</Modal>
);
return [toggle, NewCommentModal];
};
export default useNewCommentModal;
When I type the modal keeps reopening. When I remove this:
onChangeText={text => setComment(text)}
The problem goes away but obviously the state isn't updated anymore. How
come the model keeps reopening?
--EDIT--
const [toggleModal, NewCommentModal] = useNewCommentModal(
'user',
route.params.user.id
);
<NewCommentModal />
Every time your useNewCommentModal hook runs, it creates a new function called NewCommentModal which you then use as component <NewCommentModal /> (this part is very important). To React, every new NewCommentModal is different from a previous one (because you create new function each time and every render), React would run equality comparison between old and new NewCommentModal which will return false, so React will unmount old modal and mount a new one in it's place. That happens because you call function as component. So you should return not NewCommentModal component from your hook but a function that will render something instead (renderNewCommentModal) and call it not as a component but as a function ({renderNewCommentModal()}) instead. Or better yet, return only data from a hook, and use this data to render something in your main component
See my previous answer on this matter for more detailed explanation https://stackoverflow.com/a/60048240/8390411

How can I call a React-Native navigator from an outside MeteorListView file?

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!

Insert the pressed item into an array in react-native

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.

Categories

Resources