Lottie ref in Expo (react native) - react-native

I am trying to move my existing React Native (typescript) project to Expo. Everything is working fine in Expo except modal with Lottie animation ( without expo it is working properly).
My code:
export const SuccessModal = ({isVisible = false, onAnimationFinish}: Props) => {
let animation: any = React.createRef();
useEffect(() => {
if (isVisible) {
animation.current.play();
}
}, []);
return (
<Modal
visible={isVisible}
backdropStyle={{backgroundColor: 'rgba(230, 228, 253, 0.5)'}}>
<LottieView
ref={animation}
source={require('../assets/success-lottie.json')}
style={{width: 300, height: 300}}
autoPlay={true}
loop={false}
onAnimationFinish={onAnimationFinish}
/>
</Modal>
);
};
I guess the problem is with ref={animation} , because modal is being displayed, but animation is not moving - seems like animation.current.play() is not invoked in the effect. I tried useRef(null) and useRef<LottieView | null>(null) as suggested in other posts. Also tried to reinstall lottie with command: expo install lottie-react-native.
Any ideas what might be wrong here? What is so specific for Expo?

I think calling animation.current.reset(); before animation.current.play(); would do the trick.
I am using const animation = useRef(null) hook and got it working with Expo too.
However, initial play() was not working by default and I had did this trick to make it work as soon as component loads.
...
const animation = useRef(null);
...
useEffect(() => {
animation.current?.reset();
animation.current?.play();
}, []);

Related

How to animate expo-linear-gradient with reanimated?

I am using react-native-reanimated and want to animate the colors of my expo-linear-gradient. Unfortunately, nothing changes. I also created a Expo Snack.
import * as React from 'react';
import { View, Button } from 'react-native';
import Animated, {
interpolateColor,
useAnimatedProps,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient);
export default function App() {
const colorsValue = useSharedValue(1);
const animatedProps = useAnimatedProps(() => {
return {
colors: [
interpolateColor(colorsValue.value, [0, 1], ['#FFFFFF', '#000000']),
interpolateColor(colorsValue.value, [0, 1], ['#FFFFFF', '#00ff00']),
],
};
});
return (
<View>
<AnimatedLinearGradient
animatedProps={animatedProps}
style={{ height: 400, width: '100%' }}
/>
<Button
title="Change colors"
onPress={() => (colorsValue.value = withTiming(0, { duration: 2000 }))}
/>
</View>
);
}
Am I using animatedProps wrongly here? Any help would greatly be appreciated.
unfortunately, I'm not sure you can do that. You're using correctly the useAnimatedProps hook, but usually it doesn't work for all properties.
If you want to achieve this type of animation, I highly recommend you to try the LinearGradient component from #shopify/react-native-skia package.
You can easily reuse the same logic, but with the Skia values:
const colorsValue = useSharedValue(1);
const skiaFirstColor = useValue(0);
const skiaSecondColor = useValue(0);
useSharedValueEffect(() => {
skiaFirstColor.current = interpolateColor(colorsValue.value, [0, 1], ['#FFFFFF', '#000000']);
skiaSecondColor.current = interpolateColor(colorsValue.value, [0, 1], ['#FFFFFF', '#00ff00']);
}, colorsValue); // you can pass other shared values as extra parameters
const colors = useComputedValue(() => {
return [skiaFirstColor.current, skiaSecondColor.current]
}, [skiaFirstColor, skiaSecondColor])
return (<...
<Canvas>
<LinearGradient colors={colors} />
</Canvas>
</>)
Let me know if it helps.
I think it is not possible to do this with expo-linear-gradient because under the hood of this component on ios for example is CALayer and if you check reanimated docs for useAnimatedProps it says that:
Only "native" properties of "native views" can be set via useAnimatedProps. The most common usecase for this hook is when we want to animate properties of some third-party native component, since most of the properties for the core React Native components are a part of the styles anyways (at least the properties for which it makes sense to be animated). You can use the following functions to animate properties that Reanimated don't support by default:
addWhitelistedNativeProps() is used to animate properties that trigger layout recalculation, you can find them here.
addWhitelistedUIProps() is used for properties that are updated directly on the UI thread, currently allowed props are listed here.
Also looks like it is not possible to do with SVG - https://github.com/software-mansion/react-native-reanimated/issues/690
Maybe this example can help you to build your animation with SVG https://github.com/FullstackStation/react-native-svg-animated-linear-gradient

React Native on Android focus doesn't open the keyboard

We are facing some very weird issue with opening keyboard on Android device.
It works great on iOS, 90% on Android devices, but on some devices it simply don't work = the keyboard don't show despite the input field has focus.
We tried it with different approaches (with refs, with timeout and without ref either).
All with the same result :/. One of the devices we are trying has API 30, so it even doesn't seem to be an old API problem.
Have anybody facing similar issue? What can cause the issue? Any tips how to debug it?
Here is code:
export const AddNotificationModal: AddNotificationModalT = ({
visible,
category,
onCloseModal,
}) => {
const input = useRef<TextInput>(null);
...
useEffect(() => {
if (visible) {
// 1.
// InteractionManager.runAfterInteractions(() => {
// if (input?.current) {
// input.current.focus();
// }
// });
// 2.
input.current?.focus();
// 3.
// setTimeout(() => input.current?.focus(), 150);
}
}, [visible]);
return (
<Modal
name={AddNotificationModal.name}
visible={visible}>
...
<View>
<TextInput autoFocus={true} ref={input} />
<Button dark onPress={() => onConfirm()}>
{t('shared:buttons.safe')}
</Button>
</View>
</Modal>
);
};

Fonts are not loaded in my React-Native project-Android

I think something went wrong when I reorganized my RN project in order to export APK. Here is the issue:
I'm using checkbox and some other icons from #expo//vector-icons in one of my components.
Everytime I launch it, I have a font which is missing. Feather, MaterialCommunityIcons,...
I face the same issue in my searchbar in an other component.
Checkboxes and icons look like after i Dismiss the error in my app
I'm really trying hard to fix it (for hours) and to import my font in some way or an another...
There is the beginning of my code if someone see what is wrong ... Thank you all !
"sdkVersion": "35.0.0",
componentDidMount() {
this.loadAssetsAsync()
}
loadAssetsAsync = async () => {
await Font.loadAsync({
MaterialCommunityIcons,
Feather: require('../assets/Fonts/Feather.ttf'),
FontAwesome: require('../assets/Fonts/FontAwesome.ttf'),
'Material Icons': require('../assets/Fonts//MaterialIcons.ttf'),
'MaterialIcons': require('../assets/Fonts//MaterialIcons.ttf'),
'MaterialCommunityIcons': require('../assets/Fonts//MaterialCommunityIcons.ttf'),
'Material-Community-Icons': require('../assets/Fonts//MaterialCommunityIcons.ttf'),
})
}
The fatal error
A warning
The fonts are loading async AFTER the component was mounted. So you need to wait for fonts before rendering your component's childs. So you can try somthing like
loadAssetsAsync = async () => {
await Font.loadAsync({
MaterialCommunityIcons,
Feather: require('../assets/Fonts/Feather.ttf'),
FontAwesome: require('../assets/Fonts/FontAwesome.ttf'),
'Material Icons': require('../assets/Fonts//MaterialIcons.ttf'),
'MaterialIcons': require('../assets/Fonts//MaterialIcons.ttf'),
'MaterialCommunityIcons': require('../assets/Fonts//MaterialCommunityIcons.ttf'),
'Material-Community-Icons': require('../assets/Fonts//MaterialCommunityIcons.ttf'),
})
this.setState({fontsLoaded: true})
}
render(){
if(this.state.fontsLoaded){
return(
// Your component
)
}
else{
return(
<View>
<ActivityIndicator />
</View>
)
}
}

React Native Switch not animating when using React Navigation

I am trying to add a Switch to my App, and its animation is not working. I tried to create a Snack and noticed that it works without navigation (see this snack) but doesn't work when it is inside a Screen (see this snack).
It seems to be a problem just in Android.
The Switch code is just it:
const MySwitch = () => {
const [value, setValue] = React.useState(false);
return <Switch value={value} onValueChange={setValue} />;
};
Am I doing something wrong? Is it a problem with Expo, React Navigation or Switch?
In the worst case, how can I animate the component on my own?
Current behavior / Expected behavior:
React Navigation versions:
"#react-navigation/native": "^5.7.2",
"#react-navigation/bottom-tabs": "^5.7.2",
came upon this bug as well, exactly as described.
I implement a settings page where if you toggle one option, another toggle option is displayed. The second Toggle does not suffer from this bug, and this is 100% consistent. So my guess is that he bug is triggered only for Switch components that are rendered on the initial render pass somehow.
as a workaround for this bug, introducing a small delay of 10ms for rendering Switch completely resolves the issue without visible side effects.
function useDelay(ms: number) {
const [gate, setGate] = useState(false);
useEffect(() => {
setTimeout(() => {
setGate(true);
}, ms);
});
return gate;
}
const delayRender = useDelay(10);
return (
<>
{delayRender && (
<Switch />
)}
</>
);

How can I send a message from the WebView to React Native?

I’ve successfully managed to send a message from React Native (RN) to a WebView.
What I’m struggling with, is getting the message back from the WebView to RN. There’s no errors showing - it’s just that the message never gets through.
Here is the code which I’m using:
React Native Code
<WebView
ref={webview => (this.webview = webview)}
source={{ uri: "http://www.my-web-site"}}
onLoadEnd={() => this.onLoadEnd()}
onMessage={this.onMessage}
cacheEnabled={false}
originWhitelist={['*']}
javaScriptEnabled={true}
/>
onLoadEnd() {
this.webview.postMessage("RN message");
}
onMessage(message) {
console.log("I can’t see this message!");
}
WebView Code
document.addEventListener("message", function (event) {
setTimeout(function(){document.postMessage("WebView message")}, 3000);
}, false);
Please make sure that the data for each receiver is in and use the data that You need.
And always check the prescribed documents to see how to use parameters and grammar before using them.
RN
onLoadEnd() {
this.webview.postMessage("sendmessage");
}
onMessage(event) {
alert(event.nativeEvent.data);
}
WebView Code
document.addEventListener("message", function (event) {
window.postMessage(event.data);
});
React-native version 5.0 or later
window.ReactNativeWebView.postMessage(event.data);
Oh, at last, I finally came across the answer. It was a line of code which I had originally been trying to use to send a message from RN to the WebView. Turns out, it was the code required for sending from the WebView to RN:
WebView Code
document.addEventListener("message", function (event) {
window.ReactNativeWebView.postMessage(event.data);
}, false);
RN Code
<WebView onMessage={event => console.log(event.nativeEvent.data)} />
This works.
React Native
<WebView source={{ ... }}
containerStyle={{ ... }}
onMessage={ e => { console.log(e.nativeEvent.data) } }
/>
WebView
if(window.ReactNativeWebView) {
// send data object to React Native (only string)
window.ReactNativeWebView.postMessage(JSON.stringify(dataObject))
}
If you want to send complete object from webview to react-native android app then you need to stringify your object first
// Reactjs webapp
onClickSendObject = (item) => {
if(window.ReactNativeWebView) {
window.ReactNativeWebView.postMessage(JSON.stringify(item))
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
In react-native app use onMessage (for functional component)
<WebView
ref={webview => (this.webview = webview)}
source={{ uri: "give source url i.e your webapp link"}}
onMessage={m => onMessage(m.nativeEvent.data)} // functional component and for class component use (this.onMessage)
cacheEnabled={false}
originWhitelist={['*']}
javaScriptEnabled={true}
/>
// onMessage function
const onMessage = (m) => {
const messageData = JSON.parse(m);
console.log(messageData)
}
(window["ReactNativeWebView"] || window).postMessage('hello motiur dear','*');