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

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}

Related

How to implement focus/blur response in a custom react-native TextInput

I implemented a custom react-native TextInput backed by a native library. It's working pretty well except that when I tap outside of the textfield, it doesn't blur automatically and the keyboard doesn't disappear. I also tried with Keyboard.dismiss(), it doesn't work either. I looked at the 'official' TextInput implementation to replicate it without any success.
I added this code in my custom implementation (componentDidMount)
if (this.context.focusEmitter) {
this._focusSubscription = this.context.focusEmitter.addListener(
'focus',
el => {
if (this === el) {
this.requestAnimationFrame(this.focus);
} else if (this.isFocused()) {
this.blur();
}
},
);
if (this.props.autoFocus) {
this.context.onFocusRequested(this);
}
} else {
if (this.props.autoFocus) {
this.requestAnimationFrame(this.focus);
}
}
and I also defined the required contextTypes
static contextTypes = {
focusEmitter: PropTypes.instanceOf(EventEmitter)
}
code from TextInput
The problem I have is that the focusEmitter is undefined in the context and I have no idea from where it's provided in the context nor if it's actually the way it works for the regular TextInput. The only occurence of focusEmitter I could find in the react-native repo is in NavigatorIOS which I don't even use in my app.
Could anyone clarify this to me?
The simpler way to do what you want is to use Keyboard.dismiss() on a TouchableWithoutFeedback just like following example:
import {Keyboard} from 'react-native';
...
render(){
return(
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<View>
// Return everything here
<TextInput />
</View>
</TouchableWithoutFeedback>
)
}
So when you tap outside the input it will dismiss keyboard and blur the TextInput.

react-native scrollView - scrollToEnd - on Android

I'm trying to call a function that will fire upon onFoucs on TextInput that will scroll the scrollView all the way down (using scrollToEnd())
so this is my class component
class MyCMP extends Component {
constructor(props) {
super(props);
this.onInputFocus = this.onInputFocus.bind(this);
}
onInputFocus() {
setTimeout(() => {
this.refs.scroll.scrollToEnd();
console.log('done scrolling');
}, 1);
}
render() {
return (
<View>
<ScrollView ref="scroll">
{ /* items */ }
</ScrollView>
<TextInput onFocus={this.onInputFocus} />
</View>
);
}
}
export default MyCMP;
the component above works and it does scroll but it takes a lot of time ... I'm using setTimeout because without it its just going down the screen without calculating the keybaord's height so it not scrolling down enough, even when I keep typing (and triggering that focus on the input) it still doesn't scroll all the way down.
I'm dealing with it some good hours now, I did set the windowSoftInputMode to adjustResize and I did went through some modules like react-native-keyboard-aware-scroll-view or react-native-auto-scroll but none of them really does the work as I need it.
any direction how to make it done the right way would be really appreciated. thanks!
Rather than using a setTimeout you use Keyboard API of react-native. You add an event listener for keyboard show and then scroll the view to end. You might need to create some logic on which input is focused if you have more than one input in your component but if you only have one you can just do it like the example below.
Another good thing to do is changing your refs to functional ones since string refs are considered as legacy and will be removed in future releases of react. More info here.
class MyCMP extends Component {
constructor(props) {
super(props);
this.scroll = null;
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
}
_keyboardDidShow() {
this.scroll.scrollToEnd();
}
render() {
return (
<View>
<ScrollView ref={(scroll) => {this.scroll = scroll;}}>
{ /* items */ }
</ScrollView>
<TextInput />
</View>
);
}
}
export default MyCMP;
If you have a large dataset React Native docs is telling you to go with FlatList.
To get it to scroll to bottom this is what worked for me
<FlatList
ref={ref => (this.scrollView = ref)}
onContentSizeChange={() => {
this.scrollView.scrollToEnd({ animated: true, index: -1 }, 200);
}}
/>

Unknown named Module, using component will receive props to update an image

Hi I am running into the error "Unknown named Module" when trying to dynamically update image using componentWillReceiveProps. Essentially I have a topics component which has a list of topics, when a topic is clicked it gives props to another component (thumbnails) and the images related to that topic are populated.
Here is some code for the thumbnails component:
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, Button } from 'react-native';
import Player from './player.js';
import styles from '../stylesheet.js';
let baseurl = '../assets/thumbnails/';
let extension = '.jpeg';
export default class Thumbnails extends Component {
constructor(props){
super(props);
this.state = {
current: [
require('../assets/thumbnails/narcolepsy-1.jpeg'),
require('../assets/thumbnails/narcolepsy-2.jpeg'),
require('../assets/thumbnails/narcolepsy-3.jpeg')
]
}
}
componentWillReceiveProps(nextProps) {
setTimeout(()=>{
let topic = nextProps.current.toLowerCase();
let current = [];
for(let i = 1; i <= 3;i++){
current.push(require(baseurl + topic + '-' + i + extension));
}
this.setState({current,})
},1000)
}
render() {
const thumbnails = this.state.current.map((path,i) => {
return(<Image
source={path}
style={styles["thumbnail"+(i+1)]}
key={"thumbnail"+i} />);
})
return(
<View style={{flexDirection:'row'}}>
{thumbnails}
</View>
)
}
}
I've found a similar question (React-native image - unknown named module '../img/2.jpeg') that says to use source={uri: 'file.extension'};
and to keep image assets in the folder android/app/src/main/res/drawable
However I do not have an android folder, as I am using CRNA and Expo.io. Here is my project structure, please tell me what to do in this context:
App.js app.json my-app-key.keystore stylesheet.js
App.test.js assets node_modules yarn.lock
README.md components package.json
Using dynamic require calls is not supported by the React Native packager. This is outlined in the docs: React Native - Images
In order for this to work, the image name in require has to be known statically.
// GOOD
<Image source={require('./my-icon.png')} />
// BAD
var icon = this.props.active ? 'my-icon-active' : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />
// GOOD
var icon = this.props.active ? require('./my-icon-active.png') : require('./my-icon-inactive.png');
<Image source={icon} />
I would suggest creating a static data structure to hold your images, such as an object like:
const images = {
narcolepsy: [
require('../assets/thumbnails/narcolepsy-1.jpeg'),
require('../assets/thumbnails/narcolepsy-2.jpeg'),
require('../assets/thumbnails/narcolepsy-3.jpeg')
],
apnea: [
require('../assets/thumbnails/apnea-1.jpeg'),
require('../assets/thumbnails/apnea-2.jpeg'),
require('../assets/thumbnails/apnea-3.jpeg')
]
};
This way, the packager can load your references up when the bundle is created.

React Native/Shoutem: navigateBack() not working

My Problem is that I would like to navigateBack() from the BountyDetailsScreen to the LoyaltyScreen, but the navigateBack() function call does not trigger any action. When I log the function it says:
The only thing I notice is, that the navigationStack is empty. When I do the same with the navigateTo function it is working, but then I have a messed up navigation stack.
In my LoyaltyScreen.js I am displaying a ListView. It is a RN ListView (not imported from shoutem).
LoyaltyScreen.js
renderRow(bounty) {
return (
<ListBountiesView
key={bounty.id}
bounty={bounty}
onDetailPress={this.openDetailsScreen}
redeemBounty={this.redeemBounty}
/>
);
}
ListBountiesView.js
The ListBountiesView renders each ListView Row and opens a Detail Screen when clicked on the Row.
render() {
const { bounty } = this.props;
return (
<TouchableOpacity onPress={this.onDetailPress}>
{bounty.type == 0 ? this.renderInShopBounty() : this.renderContestBounty()}
<Divider styleName="line" />
</TouchableOpacity>
);
}
BountyDetailsScreen.js
In the BountyDetailsScreen I display detailed information and would like to navigateBack() to the Loyalty Screen when I press a button.
<Button styleName="full-width" onPress={() => this.onRedeemClick()}>
<Icon name="add-to-cart" />
<Text>Einlösen</Text>
</Button>
onRedeemClick() {
const { bounty, onRedeemPress } = this.props;
onRedeemPress(bounty);
navigateBack();
}
navigateBack is an action creator. You need to map it to props and read it from props in your redeemClick function. Just executing the imported action creator won't do anything since it's not connected to Redux.
Here's an example of you map it to props:
export default connect(mapStateToProps, { navigateBack })(SomeScreen));
Here's how you use it:
const { navigateBack } = this.props;
navigateBack();
I can see that airmiha's answer is what you're looking for, but I just wanted to add onto it.
You can also use hasHistory to set up your #shoutem/ui NavigationBar (if you're using it) with a simple back button that utilises navigateBack().
<NavigationBar
styleName="no-border"
hasHistory
title="The Orange Tabbies"
share={{
link: 'http://the-orange-tabbies.org',
text: 'I was underwhelmed by The Orange Tabbies, but then I looked at that
sweet, sweet back button on the Nav Bar.
#MakeNavBarsGreatAgain',
title: 'Nevermind the cats, check the Nav Bar!',
}}
/>
You can find more examples with the NavigationBar component here.

React Native: Updating state in onLayout gives "Warning: setState(...): Cannot update during an existing state transition"

I have a component in React Native which updates it's state once it knows what size it is.
Example:
class MyComponent extends Component {
...
render() {
...
return (
<View onLayout={this.onLayout.bind(this)}>
<Image source={this.state.imageSource} />
</View>
);
}
onLayout(event) {
...
this.setState({
imageSource: newImageSource
});
}
...
}
This gives the following 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.
I guess the onLayout function is called while still rendering (which can be good, the sooner the update, the better). What is the correct way to solve this problem?
Thanks in advance!
We got around this by using the measure function, you will have to wait until the scene is fully complete before measuring to prevent incorrect values (i.e. in componentDidMount/componentDidUpdate). Here's an example:
measureComponent = () => {
if (this.refs.exampleRef) {
this.refs.exampleRef.measure(this._logLargestSize);
}
}
_logLargestSize = (ox, oy, width, height, px, py) => {
if (height > this.state.measureState) {
this.setState({measureState:height});
}
}
render() {
return (
<View ref = 'exampleRef' style = {{minHeight: this.props.minFeedbackSize}}/>
);
}
Here is a solution from documentation for such cases
class MyComponent extends Component {
...
render() {
...
return (
<View>
<Image ref="image" source={this.state.imageSource} />
</View>
);
}
componentDidMount() {
//Now you can get your component from this.refs.image
}
...
}
But for my opinion it's better to do such things onload