Populte WYSIWYG editor after react native fetch - react-native

I am trying to incorporate this WYSIWYG package into my react native project (0.64.3). I built my project with a managed workflow via Expo (~44.0.0).
The problem I am noticing is that the editor will sometimes render with the text from my database and sometimes render without it.
Here is a snippet of the function that retrieves the information from firebase.
const [note, setNote] = useState("");
const getNote = () => {
const myDoc = doc(db,"/users/" + user.uid + "/Destinations/Trip-" + trip.tripID + '/itinerary/' + date);
getDoc(myDoc)
.then(data => {
setNote(data.data()[date]);
}).catch();
}
The above code and the editor component are nested within a large function
export default function ItineraryScreen({route}) {
// functions
return (
<RichEditor
onChange={newText => {
setNote(newText)
}}
scrollEnabled={false}
ref={text}
initialFocus={false}
placeholder={'What are you planning to do this day?'}
initialContentHTML={note}
/>
)
}
Here is what it should look like with the text rendered (screenshot of simulator):
But this is what I get most of the time (screenshot from physical device):
My assumption is that there is a very slight delay between when the data for the text editor is actually available vs. when the editor is being rendered. I believe my simulator renders correctly because it is able to process the getNote() function faster.
what I have tried is using a setTimeOut function to the display of the parent View but it does not address the issue.
What do you recommend?

I believe I have solved the issue. I needed to parse the response better before assigning a value to note and only show the editor and toolbar once a value was established.
Before firebase gets queried, I assigned a null value to note
const [note, setNote] = useState(null);
Below, I will always assign value to note regardless of the outcome.
if(data.data() !== undefined){
setNote(data.data()[date]);
} else {
setNote("");
}
The last step was to only show the editor once note no longer had a null value.
{
note !== null &&
<RichToolbar
style={{backgroundColor:"white", width:"114%", flex:1, position:"absolute", left:0, zIndex:4, bottom: (toolbarVisible) ? keyboardHeight * 1.11 : 0 , marginBottom:-40, display: toolbarVisible ? "flex" : "none"}}
editor={text}
actions={[ actions.undo, actions.setBold, actions.setItalic, actions.setUnderline,actions.insertLink, actions.insertBulletsList, actions.insertOrderedList, actions.keyboard ]}
iconMap={{ [actions.heading1]: ({tintColor}) => (<Text style={[{color: tintColor}]}>H1</Text>), }}
/>
<RichEditor
disabled={disableEditor}
initialFocus={false}
onChange={ descriptionText => { setNote(descriptionText) }}
scrollEnabled={true}
ref={text}
placeholder={'What are you planning to do?'}
initialContentHTML={note}
/>
}
It is working properly.

Related

My UI text not being updating whereas Hook is being updating in react-native?

Hello Folks I am using react hook to update my UI when value get change, but I am facing one issue.
This is my code :-
{showAvailableBalance ? (
<GradientText
inputWidth={dynamicTextWidth(userBalance, 32)}
inputHeight={44}
maskedStyle={styles.flex0}
text={userBalance || '0'}
textSystem={'number-large'}
textAlign={'center'}
/>
) : (
<Text
text={parseFloat(
parseFloat(user?.balance) + onChainBalance,
).toFixed(2)}
textSystem={'number-large'}
color={Colors.GRAY3}
style={{ marginLeft: 8 }}
/>
)}
To show user balance I have used userBalance hook and here I am updating this hook.
const [userBalance, setUserBalance] = useState(user?.balance);
useEffect(() => {
console.log('The user updated balance is = ', user?.balance);
setUserBalance(user?.balance);
}, [user?.balance]);
However this useEffect run when user.balance get update but my UI text not being change.
What I am doing wrong can anyone please sugggest me ?
==> Check below code and let me know.
const [userBalance, setUserBalance] = useState(user?.balance);
useEffect(() => {
console.log('The user updated balance is = ', user?.balance);
setUserBalance(user?.balance);
}, [showAvailableBalance,user?.balance]);

Execute a function based on a specific scroll position in React native

I am trying to call an api base on scroll View current position but not sure how to I achieve that.
This is my code
<ScrollView
ref={scrollViewRef}
scrollEventThrottle={0}
onScroll={({nativeEvent}) => {
console.log(
nativeEvent.contentSize.height -
nativeEvent.layoutMeasurement.height,
);
console.log(nativeEvent.contentOffset);
}}>
I tried to call the api inside onScroll but that didnt work well.
Try adding an event listener at the particular scroll location you want the function to execute.
useEffect(() => {
Window.addEventListener(‘scroll’_.debounce(setScroll,1000));},[]);
I have solved the issue by using adding an if check. If the api data exist then the function wont execute anymore.
here's the code
const [apiData, setApiData] = useState();
onScroll={({nativeEvent}) => {
if (!apiData) {
if (
nativeEvent.contentSize.height -
nativeEvent.layoutMeasurement.height -
nativeEvent.contentOffset.y <
250
) {
getApiDataHandler();
}
}
}}

Creating a checkbox group with React Native

Good Morning! I am wanting to create a selection box where the user has several options of items to choose from and when clicking on a button, it triggers a function that shows all the values that the user chose in the form of an array, json or even arrays ( hard task).
In the React Native documentation, only simple examples of checkboxes using the component are provided and I wanted to go much further than the documentation provides me. What are the possible solutions to this problem? (from a simpler example to an advanced one) and what (s) ways can I explore this problem in order to solve it in the most practical and uncomplicated way?
Definitions and examples of official documentation:
https://reactnative.dev/docs/checkbox/ (CheckBox)
https://reactnative.dev/docs/button/ (Button)
With this problem, another one came up: build an application where the user selects shopping options (items) and a subtotal is displayed in the lower corner of the application as he selects or deselects the items he is going to buy, and there is also an option to reset the subtotal by returning it to the zero value.
From the problem mentioned at the beginning, what are the possible solutions to create this application previously mentioned in a practical and simple way?
Multi Checkbox example ( Updated with Hook )
export const Example = () => {
const [checkboxes, setCheckboxes] = useState([{
id: 1,
title: 'one',
checked: false,
}, {
id: 2,
title: 'two',
checked: false,
}]);
const onButtonPress = () => {
const selectedCheckBoxes = checkboxes.find((cb) => cb.checked === true);
// selectedCheckBoxes will have checboxes which are selected
}
const toggleCheckbox = (id, index) => {
const checkboxData = [...checkboxes];
checkboxData[index].checked = !checkboxData[index].checked;
setCheckboxes(checkboxData);
}
render(){
const checBoxesView = checkboxes.map((cb, index) => {
return (
<View style={{flexDirection:"row"}}>
<Checkbox
key={cb.id}
checked={cb.checked}
onPress={() => toggleCheckbox(cb.id, index)} />
<Text>{cb.title}</Text>
</View>
);
});
return (
<View>
{ checBoxesView }
<Button onPress={onButtonPress} title="Click" />
</View>
);
}
}

React Native: Checkbox List Structure

A user object has an array prop schools that references one or more school objects. I would like to use a <List> with <CheckBox> to mutate the schools array.
I load the user object into the view, and I load the listOfSchools (from the application state) to generate the checkbox list:
<List data={listOfSchools} keyExtractor={ item=> item._id } renderItem={({item})=>renderItem(item)} />
The renderItem function:
const renderItem = (school) => {
return <ListItem
title={school.name}
accessory={()=>renderAccessory(school)}
/>
};
The renderAccessory function:
const renderAccessory = (school) => {
return <CheckBox checked={() => checkSchool(school._id)} onChange={()=>changeSchool(school._id)} />
}
The checkSchool function returns boolean on if the school._id is referenced in the user.schools array. The changeSchool function adds or removes the school._id from the users.schools array.
The changeSchool function:
const changeSchool = (schoolId) => {
let checked = checkSchool(schoolId);
if (!checked) {
// add schoolId to user.schools
} else {
// remove schoolId from user.schools
}
}
This drastically does not work. It appears that no matter what I use to mutate the state, the checkboxes never update, nor does the user.schools array mutate.
What is the proper way to structure such a design goal?
Assuming that you use UI Kitten, I can see that you got the checked prop value wrong for the CheckBox component.
UI Kitten CheckBox reference
The checked prop needs to be a boolean not a Callable as you have it there
I would try to change the code like this:
const renderAccessory = (school) => {
const isChecked = checkSchool(school._id);
return <CheckBox checked={isChecked} onChange={()=>changeSchool(school._id)} />
}
Let me know if that helped.
While trying various solutions i can conclude few things here:
With the solution given by #Cornel Raiu the checked and unchecked flags are getting correctly calculated however, the display was not correct with the state of checked/unchecked
I replaced Checkbox with Toggle, just to be sure that it works with iOS too
PROBLEM that i faced still is that, even the State of item getting toggled is correctly populating it was getting reset
The outside container of Toggles is List and ListItem,
OBSERVATION is that the Press event on List was actually getting the Checkbox/Toggle into correct Display State...
SOLUTION:
After longer time of research and experiments I got my thing working with following approach -
I maintained separate collection of Checked Items
There is already a state of Collection of master items, as input to List
Every time the Checkbox/Toggle is clicked, the master list of Data is cloned and copied back to its state
This was triggering the slight re-render of component and thing is working as expected.
const [cashTransactions, setCashTransactions] = useState([]); // master data
const [selectedTransactions, setSelectedTransactions] = useState([]); // selected data
const renderItem = ({ item, index }) => (
<ListItem
title={'('+item.id + ') ' + item.firstName + ' ' + item.lastName}
description={Moment(item.createdOn).format('yyyy-MM-DD hh:mm:ss')}
accessoryLeft={selectedTransactions.includes(item.id) ? RadioOnIcon : RadioOffIcon}
accessoryRight={() => checkBoxSpace(item)}
/>
);
const checkBoxSpace = (item) => {
let itemChecked = selectedTransactions.includes(item.id);
return (
<View style={styles.actionContainer}>
<Button size='tiny' status='basic' accessoryLeft={rupeeSymbol}>{item.amount}</Button>
<Toggle checked={itemChecked} status='primary' size='small' onChange={checked => checkboxChecked(item, checked)}></Toggle>
</View>
)
}
const checkboxChecked = (item, checked) => {
console.log('Item -' + item.id + ' ' + checked);
if (checked) {
if (!selectedTransactions.includes(item.id)) {
selectedTransactions.push(item.id);
}
} else {
if (selectedTransactions.includes(item.id)) {
selectedTransactions.pop(item.id);
}
}
console.log('selectedTransactions ' + JSON.stringify(selectedTransactions));
// This is the thing i applied to get it done.
const cloned = [...cashTransactions];
setCashTransactions(cloned);
}
// View
<List
style={styles.container}
data={cashTransactions}
renderItem={renderItem}
/>

React Native Retrieve Actual Image Sizes

I would like to be able to know the actual size of a network-loaded image that has been passed into <Image /> I have tried using onLayout to work out the size (as taken from here https://github.com/facebook/react-native/issues/858) but that seems to return the sanitised size after it's already been pushed through the layout engine.
I tried looking into onLoadStart, onLoad, onLoadEnd, onProgress to see if there was any other information available but cannot seem to get any of these to fire. I have declared them as follows:
onImageLoadStart: function(e){
console.log("onImageLoadStart");
},
onImageLoad: function(e){
console.log("onImageLoad");
},
onImageLoadEnd: function(e){
console.log("onImageLoadEnd");
},
onImageProgress: function(e){
console.log("onImageProgress");
},
onImageError: function(e){
console.log("onImageError");
},
render: function (e) {
return (
<Image
source={{uri: "http://adomain.com/myimageurl.jpg"}}
style={[this.props.style, this.state.style]}
onLayout={this.onImageLayout}
onLoadStart={(e) => {this.onImageLoadStart(e)}}
onLoad={(e) => {this.onImageLoad(e)}}
onLoadEnd={(e) => {this.onImageLoadEnd(e)}}
onProgress={(e) => {this.onImageProgress(e)}}
onError={(e) => {this.onImageError(e)}} />
);
}
Thanks.
Image component now provides a static method to get the size of the image. For example:
Image.getSize(myUri, (width, height) => {this.setState({width, height})});
You can use resolveAssetSource method from the Image component :
import picture from 'pathToYourPicture';
const {width, height} = Image.resolveAssetSource(picture);
This answer is now out of date. See Bill's answer.
Image.getSize(myUri, (width, height) => { this.setState({ width, height }) });
Old Answer (valid for older builds of react native)
Ok, I got it working. Currently this takes some modification of the React-Native installation as it's not natively supported.
I followed the tips in this thread to enabled me to do this.
https://github.com/facebook/react-native/issues/494
Mainly, alter the RCTNetworkImageView.m file: add the following into setImageURL
void (^loadImageEndHandler)(UIImage *image) = ^(UIImage *image) {
NSDictionary *event = #{
#"target": self.reactTag,
#"size": #{
#"height": #(image.size.height),
#"width": #(image.size.width)
}
};
[_eventDispatcher sendInputEventWithName:#"loaded" body:event];
};
Then edit the line that handles the load completion:
[self.layer removeAnimationForKey:#"contents"];
self.layer.contentsScale = image.scale;
self.layer.contents = (__bridge id)image.CGImage;
loadEndHandler();
replace
loadEndHandler();
with
loadImageEndHandler(image);
Then in React-Native you have access to the size via the native events. data from the onLoaded function - note the documentation currently says the function is onLoad but this is incorrect. The correct functions are as follows for v0.8.0:
onLoadStart
onLoadProgress
onLoaded
onLoadError
onLoadAbort
These can be accessed like so:
onImageLoaded: function(data){
try{
console.log("image width:"+data.nativeEvents.size.width);
console.log("image height:"+data.nativeEvents.size.height);
}catch(e){
//error
}
},
...
render: function(){
return (
<View style={{width:1,height:1,overflow='hidden'}}>
<Image source={{uri: yourImageURL}} resizeMode='contain' onLoaded={this.onImageLoaded} style={{width:5000,height:5000}} />
</View>
);
}
Points to note:
I have set a large image window and set it inside a wrapping element of 1x1px this is because the image must fit inside if you are to retrieve meaningful values.
The resize mode must be 'contain' to enable you to get the correct sizes, otherwise the constrained size will be reported.
The image sizes are scaled proportionately to the scale factor of the device, e.g. a 200*200 image on an iPhone6 (not 6 plus) will be reported as 100*100. I assume that this also means it will be reported as 67*67 on an iPhone6 plus but I have not tested this.
I have not yet got this to work for GIF files which traverse a different path on the Obj-C side of the bridge. I will update this answer once I have done that.
I believe there is a PR going through for this at the moment but until it is included in the core then this change will have to be made to the react-native installation every time you update/re-install.
TypeScript example:
import {Image} from 'react-native';
export interface ISize {
width: number;
height: number;
}
function getImageSize(uri: string): Promise<ISize> {
const success = (resolve: (value?: ISize | PromiseLike<ISize>) => void) => (width: number, height: number) => {
resolve({
width,
height
});
};
const error = (reject: (reason?: any) => void) => (failure: Error) => {
reject(failure);
};
return new Promise<ISize>((resolve, reject) => {
Image.getSize(uri, success(resolve), error(reject));
});
}