how to change element that fits a component to an element that fits a function in react native - react-native

I am a new react native developer, I found a component and I want to use it in a function, but it is not clear to me how I would change it, can I get a help?
Here is the component
import TagInput from 'react-native-tag-input';
...
<TagInput
value={this.state.emails}
onChange={(emails) => this.setState({ emails })}
labelExtractor={(email) => email}
text={this.state.text}
onChangeText={(text) => this.setState({ text })}
/>
I got the code from here https://bestofreactjs.com/repo/jwohlfert23-react-native-tag-input

I guess you are asking how to adapt the code to fit the functional component, which includes converting the this.setState.
React provides some thing called React hooks, which you can think of as a way to replace states and lifecycles. You can read more about it here here
In your case, it would go like this:
import { useState } from 'react';
...
// useState can only be called inside functional components
const [emails, setEmails] = useState([]);
const [text, setText] = useState('');
...
<TagInput
value={emails}
onChange={(emailTags) => setEmails(emailTags)} // to avoid naming confusion
labelExtractor={(email) => email}
text={text}
onChangeText={(inputText) => setText(inputText)}
/>

you don't need to convert the component itself, you can use it as it is, but you need to change its implementation.
Basically, if you want to use function components, which is highly recommended now, you need to change the usage of the state in the component which will contain the <TagInput>.
Instead of using "this" which points to the class itself, you need to implement a hook called useState.
You can find it the docs here: https://reactjs.org/docs/hooks-state.html

Related

How to create an rxjs Observable from TextInput (either onChange or onTextChange)

I want to create an observable from a change event that gets fired on a React Native TextInput component. TextInput comes with 2 change props that I'm aware of (onChangeText and onChange). From what I gather, you need to use onChange if you want access to the native event you need to use onChange.
I don't know much about the native event object. I am trying to create an rxjs observable using fromEvent.
First I created a ref in my functional component like this:
const sqftRef = useRef().current
Then I attached this ref to the TextInput component like this:
<TextInput
ref={sqftRef} // attach a ref
label='Sqft'
mode='flat'
textContentType='none'
autoCapitalize='none'
keyboardType='numeric'
autoCorrect={false}
value={String(formValues.sqft)}
dense
underlineColor={colors.colorOffWhite}
onChangeText={(text) => setText(text)}
onChange={e => {
// somehow create an observable from this event ???
}}
style={styles.inputStyles}
theme={inputTheme}
/>
I tried to create an Observable using fromEvent like this but it doesn't work. I get undefined is not an object (evaluating target.addEventListener):
fromEvent(sqftRef, 'onChange').subscribe(value => console.log(value))
I know my approach is all wrong. Hoping someone can point me in the correct direction.
I would emit events you need into a subject, then subscribe to the subject in other parts of your code.
Here's a simple React example that should get you started
function App() {
const textChange = new Subject<string>();
useEffect(() => {
// subscribe to
const subscription = textChange.asObservable().subscribe(console.log)
return () => subscription.unsubscribe()
}, [])
// Emit events with a subject
return <textarea onChange={(e) => {
textChange.next(e.target.value)
}}>
</textarea>
}
render(<App />, document.getElementById('root'));
Check out the example here: https://stackblitz.com/edit/react-ts-akoyfv
I think the problem is with assigning the current directly to the sqftRef. Try to define it without current, but use current when creating the Observable, like the following:
const sqftRef = useRef();
Then create the Observable within useEffect to make sure that the DOM is ready:
useEffect(() => {
fromEvent(sqftRef.current, 'onChange').subscribe((value) =>
console.log(value)
);
});
OK, I was able to figure it out with the help of Amer Yousuf and Alex Fallenstedt.
I did something similar to what Alex suggested, modifying his solution for React Native. One reason his solution wasn't working for me is that it is important to use the useRef hook to prevent the Observable from being re-created on each render. If the observable is recreated (on a re-render) and useEffect doesn't run again, then we won't have an active subscription to the newly (re-created) observable (useEffect never runs again). That's why my call to sqft$.next was originally only being called once (the first time until we re-render).
My solution looks like this:
let sqft$ = useRef(new BehaviorSubject(0)).current
useEffect(() => {
const sub = sqft$.subscribe({
next: (val) => {
// just testing stuff out here
updateForm('sqft', val)
updateForm('lot', val * 2)
}
})
// this is only relevant to my use case
if (activeReport) sqft$.next(activeReport.sqft)
return () => sub.unsubscribe()
}, [activeReport])
and of course I call this in onChangeText:
onChangeText={(text) => {
sqft$.next(text)
}}
So this is working right now. I still feel like there may be a better way using onChange(e => ...stuff). I will leave this question open for a little bit in case anyone can break down how to do this using nativeEvent or explain to me how I can access an event off the TextInput component.

Passing Params in The Same Screen. React Native

I know how to pass parameters to other screen. For example:
<Flatlist
data={SOMEDATA}
renderItem={(itemData) => (
<Button
onPress={() => {
props.navigation.navigate("myScreen", {
myId: itemData.item.id,
})
}}
>
</Button>
/>
Then I can access myId on the myScreen screen by props.route.params.myId, But I don't know how to access it in the same screen I am using (same js file). if I write props.route.params.myId, I will get this error :
undifined is not an object (evaluating 'props.route.params.myId'
any help please?
are you trying to set a property in the state of your current screen?
if so, what you need is a state
assuming you are using a functional react component and not a class based component, what you need to do is to introduce a state to your component
const [state, setState] = useState(initialState);
when ever you want to change your state, you just need to call the setState(myNewObj) function,
whenever you want to access your state
you can just call for state
the initiateState can be anything
in your case something like
const [myId, setMyId] = useState(-1);
I suggest you take alook at the official React Documentation
https://reactjs.org/docs/hooks-reference.html#basic-hooks

unable to pass props to external const?

I am trying to create a "feed" of posts, similar to instagram/venmo/twitter. Each post is really a "Feed Cell", composed of components, displayed using a flat list. The actual "Feed Cell Class" imports each individual component, and composes the components into the post.
With this logic, I am trying to implement "Like" functionality, since this is a social networking app.
The flow goes like this:
User clicks the like button touchable opacity in the "Feed Cell Class"
<TouchableOpacity
onPress={() => {
this.like(this.state.postID)
}}>
<Text style = {styles.tradeText}> like</Text>
</TouchableOpacity>
which calls a function, "like", which is also in the "Feed Cell Class". Within this function, I call the "Like Post" imported function. I am passing the post ID to each of the functions.
import likePost from './FFCcomponents/likeButton'
like = async(postID) => {
console.log(postID)
likePost(postID);
}
Within the likeButton class, is the likePost function:
import React, { useState } from 'react'
import Firebase from '../../../firebase.js'
const likePost = ({ postID }) => {
console.log(postID)
}
export default likePost;
However, the first postID prints, and the second one is "undefined".
P4Tu4GkcuxIZ9fGn1VFG
undefined
So the props are not passing correctly from the Feed Cell Class to the imported LikePost function. This is an issue, since all the building blocks are in their specific files, and are going to be imported to the Feed Cell Class to be composed into a post.
How can I fix this issue?
Why are you here doing async in the like function?
The problem may be related to the fact that you pass the object to the likepost function, and already likepost does distructorization.
Maybe you need to make const likepost = (postID) => { ... }

React Hooks and useEffect – best practices and server issues

I am using React Native with functional components. componentDidMount() etc. are not available in functional components, instead I use Hooks. But Hooks don't act like lifecycle methods. I am wondering what the best practices are.
Assumed that we have a function like this one:
const ABCScreen = () => {
const [someHook, setSomeHook] = useState<any>()
useEffect(() => {
// some code inside this function which is called on every component update
}, [])
server.asyncCall().then(data => {
setSomeHook(data)
})
return (<View>
{someHook ? (<Text> `someHook` was assigned </Text>) : (<Text> `someHook` was not assigned, display some ActivityIndicator instead</Text>)}
</View>)
}
Where to place server.asyncCall()? Inside or outside of useEffect?
I think you have a misunderstanding here. The convention is that all the fetching data is going to be placed inside the componentDidMount lifecycle method. React useEffect hook can replace this easily by placing an empty array of dependencies, which means you can place that call inside the useEffect you already have.
Unlike you mention in your code comment, this hook won't be triggered on each component update. It will be only be triggered once the component is being mounted. So, you should be able to do it as follows:
const ABCScreen = () => {
const [someHook, setSomeHook] = useState<any>()
useEffect(() => {
server.asyncCall().then(setSomeHook)
}, [])//only triggered when component is mounted.
In the future, you might want to check the rules of the hooks.

ReactNative scrollToEnd() on FlatList - Functional Component

Am trying to implement a chat app using ReactNative. The problem am facing is, after new item is added to the FlatList, am trying to scroll the FlatList items to the bottom so that the newly added message is visible.
When I checked the documentation, I can see a method called scrollToEnd(). But not sure how to use it as am following the Functional Component style of coding. Because when I googled, I can see examples where it uses the ref in a Class Component style coding.
But couldn't find how to use it in Functional Component!
I think you're looking for useRef
// I hope you don't mind the typescript
import {useRef} from 'react';
import {FlatList} from 'react-native';
export const Comp = () => {
const flatListRef = useRef<FlatList<any>>();
// however you detect new items
flatListRef?.current?.scrollToEnd();
return (
<FlatList
data={[]}
renderItem={() => null}
ref={flatListRef}
/>
);
}
But I think if you use inverted={true} on the flat list, it should snap to the top, or better bottom (I think).
For typescript you can use just:
const listRef = useRef<FlatList>(null);
and in your code you just need to check if your ref is exist:
listRef?.current?.scrollToIndex({animated: true, index: yourIndex})