How to only load/render custom rendered image when it comes into view (react-native-render-html) - react-native

I am using react-native-render-html to render content that has images in it via a custom renderer. The issue I'm running into is some pages have dozens of images, and I would like to only download/render the ones I need that are in or near the viewport. The way the custom renderer is set up currently (via the documentation) it downloads all of them at once (How would I go about Implementing the logic into a more flatlist-like render system?
Here's the image renderer (if it helps):
const ImgRenderer = useCallback((props: any) => {
console.log('imgRendererProps: ', props)
const { Renderer, rendererProps } = useInternalRenderer('img', props);
const uri = rendererProps.source.uri;
const imgSource = {
...rendererProps.source,
// You could change the uri here, for example to provide a thumbnail.
uri: `https://home.website.com${uri?.split('about://')[1]}`
};
return (
<TouchableWithoutFeedback onPress={() => setModalImage(uri?.split('about://')[1] ?? null)}>
<Renderer {...rendererProps} source={imgSource} style={{resizeMode: 'contain'}} />
</TouchableWithoutFeedback>
);
}, [props.tnode?.init.nodeIndex])

Related

How do I get the current item displayed in a FlatList?

I'm using a flatlist to render a feed of videos for my project. I want to prevent the videos from auto-playing when they're not visible on the screen.
I figured the simplest way to do this would be to see if the current video on the screen is currently visible/active and if so, I can set props to true for playing etc.
I'm having a lot of issues trying to achieve this however an I wanted to know if anyone can give me some pointers as I'm new to React-Native.
You can use onViewableItemsChanged prop from FlatList
Docs Here
Working Example of FlatList of Videos with automatic play/pause feature
Something as shown below
const [Viewable, SetViewable] = React.useState([]);
const ref = React.useRef(null);
const onViewRef = React.useRef((viewableItems) => {
let Check = [];
for (var i = 0; i < viewableItems.viewableItems.length; i++) {
Check.push(viewableItems.viewableItems[i].item);
}
SetViewable(Check);
});
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 80 });
<FlatList
data={Videos}
keyExtractor={(item) => item._id.toString()}
renderItem={({ item }) => <VideoPlayer {...item} viewable={Viewable} />}
ref={ref}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
/>

React native Image ' Warning: Failed prop type: Invalid prop `source` supplied to `Image`. '

So this is my code
I already answered the question.
export const BigCard = ({Title, Image, Description}) => {
console.log(Image)
return(
<StyledBigCard>
<StyledImage source={{
uri: Image,
}}/>
<StyledStroke>
<StyledStrokeText>
<StyledPBigCard>{Description}</StyledPBigCard>
</StyledStrokeText>
<FontAwesomeIcon style={style.icon} size={ 22 } icon={faChevronRight} />
</StyledStroke>
</StyledBigCard>
)
};
this is were i import the images etc. it is set in PBS1Detail, its an object so when i go to PBS1Detail.Image i get the image
const db = firebase.firestore();
const [PBS1Detail, setPBS1Detail] = useState([]);
const [partnerLevel, setPartnerLevel] = useState('');
useEffect(()=> {
{/* DATABASE */}
db.collection('Track').get().then((snapshot) => {
let results = []
snapshot.docs.forEach(doc => {
results.push(renderTracks(doc))
}
)
setPBS1Detail(results)
});
then i push it all the way to the screen were i import Image
all my imports are correct, and the console.log() gives me the good url to the image.
now when i now reload the app it gives the error "Warning: Failed prop type: Invalid prop source supplied to Image."
then when i update the image component to
<StyledImage source={{uri: 'https://s3.eu-central-1.wasabisys.com/image-parttime-bijbelschool-jaar-1/Track1Module1Thumbnail.jpg'}}
and than save the changes it works
then when i update it to
<StyledImage source={{uri: Image}} />
and than save the changes it also works
so when i run it the first time it gives me this error than when i change it to an url and than change it back to the Image prop it works. but when i reload the app it gives me this error again.
how can i fix this?
The PBS1Detail is an empty array until you actually get the image data from Firestore.
So when the first render, I guess you are trying to pass undefined or some invalid values for the source prop.
How about you give a condition for the render part?
export const BigCard = ({Title, Image, Description}) => {
console.log(Image)
return(
<StyledBigCard>
{!!Image && (
<StyledImage source={{
uri: Image,
}}/>
)}
<StyledStroke>
<StyledStrokeText>
<StyledPBigCard>{Description}</StyledPBigCard>
</StyledStrokeText>
<FontAwesomeIcon style={style.icon} size={ 22 } icon={faChevronRight} />
</StyledStroke>
</StyledBigCard>
)
};
The problem is not my code above, what i did was that on my homescreen i sended wrong props to Image is send
Image={Require('../../assets/Image.png')}
thats why te error occurded. i have set it to
Image={'https://s3.eu-central-1.wasabisys.com/image-parttime-bijbelschool-jaar-1/Track1Module1Thumbnail.jpg'}
and it works perfectly!

React Native Screen refresh from nested component screen

I have a screen, PDP, that screen contains a component, TopNews. I want TopNews onclick to redraw PDP (it passes in an Article ID which the PDP uses to retrieve the article). The diagram below shows the flow
The code I have to support this inside TopNews is;
<TouchableOpacity onPress={() => navigation.navigate('Pdp', {articleid: item.id})}>
The challenge is the TouchableOpacity event triggers, but the page doesn't refresh the PDP. I don't want to refresh only the PDP as I may include the TopNews component in other screens outside of the PDP, its just in this case its inside of the screen it needs to call.
There are more then one way to do it.
You can create a state for the article id and allow the TopNews to set this state:
const PDP = () => {
const [ id, setId ] = React.useState(0);
return (
<div>
<ArticleComponent articleId={id} />
<TopNews idSetter={setId} />
</div>
);
}
const ArticleComponent = ({ articleId }) => {
return /* something that renders the article by the id */ ;
}
const TopNews = ({ idSetter }) => {
return (
<TouchableOpacity
onPress={() => {
idSetter(item.id);
navigation.navigate('Pdp')
}}>
);
}
Another more elegant way, is to use react contexts. You can create a context provider in the PDP component and change the context data in any PDP's children component.
I found the answer, instead of using navigation.navigate, use navigation.push instead;
<TouchableOpacity onPress={() => navigation.push('Pdp', {articleid: item.id})}>
This works perfectly :)

React Native is there an attribute equals to alt on image component

Coming from reactjs i was expecting "alt" attribute on the image component that will show text in case the image could not be loaded.
I read the documentation here and the closest thing I found is the on error event.
Is there an attribute equal to alt in React Native image component? And what is the easiest way to replace your image with a default text if i don't have the alt attribute?
You can make such a component yourself, it requires a very minimal amount of code. Here's a basic example:
export default class AccessibleImage extends Component {
state = {
error : false
};
_onImageLoadError = (event) => {
console.warn(event.nativeEvent.error);
this.setState({ error : true });
}
render() {
const { source, alt, style } = this.props;
const { error } = this.state;
if (error) {
return (
<Text>{alt}</Text>
);
}
return (
<Image
accessible
accessibilityLabel={alt}
source={source}
style={style}
onError={this._onImageLoadError} />
);
}
}
This will show the provided alt if there was an error loading the image and also use that text as the accessibilityLabel for screen readers which is closer to web behaviour.
A better answer than the previous if using React Native 0.68+ is to include the alt attribute on an <Image> Component like so
<Image
style={styles.yourImageStyles}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
alt={'Alternate text that will be read be screen readers'}
/>
Chatgpt said:
let displayImage;
try {
displayImage = <Image source={require('./secondImage.png')} />;
} catch (error) {
displayImage = <Text>Second Image not available</Text>;
}
and to use the image/text:
{displayImage}

React Native WebView onMessage and postMessage to get all web page

I don't really clear how to implement onMessage and postMessage, can I get whole web page but only from react native side.
I mean, I will inject this code using injectedJavaScript
var markup = document.documentElement.innerHTML
window.postMessage(markup)
and I will receive the result using onMessage. Is it posible cause so far I can't do that
yes you can do this all you to have to do is use window.postMessage("message") from your web-page that is going to load in WebView and you can see that message in onMessage prop.
Example:
class Test extends React.Component{
constructor(props){
super(props);
this.state:{
html:''
}
}
componentWillMount(){
this.setState({
html : `<html>
<head>
<script>
window.postMessage("Messga from webView")
</script>
</head>
<body><h1>Hello from webView</h1></body>
</html>`
})
}
render(){
return (
<View style={{flex: 1}}>
<WebView
ref={(reff) => {
this.webView = reff;
}}
source={{html: this.state.html}}
style={[styles.flex1, styles.padding5]}
onMessage={(event)=>{
let message = event.nativeEvent.data;
/* event.nativeEvent.data must be string, i.e. window.postMessage
should send only string.
* */
}}
onNavigationStateChange={(evt)=>{}}
onError={(e) => {
console.warn('error occured', e)
}}/>
</View>
)
}
}
I just added a sample html and rendered it in WebView, you can do the same in your page that you are going to load in WebView.
Or another solution is:
You can use injectedJavaScript or injectJavaScript props of WebView as described here.
postMessage is deprecated :: and now you have to use window.ReactNativeWebView.postMessage(data)
const injectedJavascript = `(function() {
window.postMessage = function(data) {
window.ReactNativeWebView.postMessage(data);
};
})()`;
for a full imagination of a component will be:
export const WebViewComponent = (props) => {
const webViewScript = `
setTimeout(function() {
window.ReactNativeWebView.postMessage(/*your pushed data back to onMessage 'event.nativeEvent.data'*/);
}, 2500);
true; // note: this is required, or you'll sometimes get silent failures
`;
return (
<WebView
source={{
uri: `https://example.com`,
}}
automaticallyAdjustContentInsets={false}
scrollEnabled={false}
onMessage={(event) => {
// do something with `event.nativeEvent.data`
}}
javaScriptEnabled={true}
injectedJavaScript={webViewScript}
domStorageEnabled={true}
style={{ width: "100%", height: /*webViewHeight*/ }}
/>
);
};
Actually
i was looking at the documentation since i use the injectJavascript function of the react native´s webview. And in here https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage , it says that for extensions we need to use a "*" as a property .
So it wouldn´t be like this "window.postMessage("Messga from webView")"
Instead it will need to be window.postMessage("Messga from webView","*") to work.