For the life of me, I cannot figure out why this little bit of code will not work!
I have isolated the issue to the Button element (the import statement seems fine).
I am seeing the error "Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of Login.
import React, { ScrollView, Image, StyleSheet, Button } from "react-
native";
import { connect } from "react-redux/native";
const onButtonClicked = () => {};
class Login extends React.Component {
componentDidMount() {}
render() {
return (
<ScrollView
style={{ flex: 1 }}
contentContainerStyle={{
justifyContent: "center",
alignItems: "center"
}}
>
<Image
source={require("../../img/coin.png")}
resizeMode={Image.resizeMode.cover}
style={Styles.coinLogo}
/>
<Button title="Login default" onPress={() => {}} />
</ScrollView>
);
}
}
Login.propTypes = {
dispatch: React.PropTypes.func
};
Login.defaultProps = {
dispatch: () => {}
};
const Styles = StyleSheet.create({
coinLogo: {
marginTop: 50,
height: 200,
width: 200
},
loginButton: {
marginTop: 50
}
});
export default connect(state => ({}))(Login);
This is a nasty issue because the error message is really vague. It has to do (I think) with object destructuring.
When you destructure an object, say:
var myObject = {a: 1, b: 2, c: 3};
let {a, b, c, d} = myObject;
Your transpiler does the following:
let a = myObject.a;
let b = myObject.b;
let c = myObject.c;
let d = myObject.d; // Ah! But we never defined a 'd' key, did we?
And of course, non-existing keys evaluate to undefined without raising an error, so what you get is d having a value of undefined.
Let's go back to your imports. I think they should look like this:
import React from 'react'; // The React API moved to the react package, you should be getting an error because of this. See https://github.com/facebook/react-native/releases/tag/v0.26.0 (Unless you are using React Native <= 0.25)
import { ScrollView, Image, StyleSheet, Button } from "react-native";
import { connect } from "react-redux"; // As far as I know, the connect is exported directly from 'react-redux', but it might be OK the way you had it.
Now, let's go to your render. We are trying to render a ScrollView, a Image and a Button. RN is raising the error because one of those is being evaluated to undefined, which is not allowed, but it is not telling us which one. You can try and console.log the values of the three and check which one is undefined. However, I strongly think it is the Button, because it was added in RN 0.37, and as I mentioned before in the import of React, you must be running a version of RN previous to the 0.26.0 tag, otherwise the code would have raised a different error.
Let me know if that is the case.
Related
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
In my app, I have a function which calls every 2s a bluetooth command to ask the current temperature of a device with a setInterval function.
The bluetooth response is given by monitored function. I use react native-ble-plx library for that.
I have no problem with this process.
The temperature is returned via a property which is dispatched via redux in an action file.
But when I "dispatch" (via redux) the function to my screen, I have a short interrupt which causes a laggy/jerky behavior. In my case, I have a slide to unlock button, and on my device when the dispatch is call, the touch operation is interrupted, and become not intuitive and annoying. It's difficult to explain the problem, but my question is simple, how I have to set react-redux not to be laggy, or not interrupt current user interaction on redux dispatch ?
My app, is based on this project structure (for react-redux with Ble) : https://github.com/momolarson/BLEServiceDiscovery
Environement:
react-native: 0.63.3
react-native-ble-plx: 2.0.2
react-redux: 7.2.1
This is pseudo code of my app (the code is more longer, but I have excluded all other by remove them) :
HomeScreen.js
import stuff[...]
class HomeScreen extends Component {
componentDidMount() {
this.timer = setInterval(() => {
this.props.readTemp();
}, 2000);
}
render() {
const { value } = this.state
return (
<>
<ScrollView>
<Text>{this.props.temperatture}"></Text>
<Slide2Unlock/>
</ScrollView>
</>
);
}
}
function mapStateToProps(state) {
return {
temperature: state.temperature,
};
}
const mapDispatchToProps = dispatch => ({
readTemp: () => bluetooth.readTemp(),
})
export default connect(mapStateToProps, mapDispatchToProps())(HomeScreen);
redux's action file : actionBt.js (my file is based on this https://github.com/momolarson/BLEServiceDiscovery/blob/master/actions/index.js)
[...]
device.monitorCharacteristicForService(
characteristicData.serviceUUID,
characteristicData.uuid,
(error, characteristic) => {
if (characteristic != null && characteristic.value != null) {
dispatch(formatTemperature(characteristic.value));
}
},
);
thanks for your help
Update 1
I make a specific version of my app, without bluetooth, just the slide to unlock module and a watcher with setInterval, and still have a laggy behavior, when the state is dispatched. I have done tests with button only, when I tap then show the value via dispatch, it's still the same trouble.
this my test code, index.js (redux action file)
export const readTemp = () => {
return (dispatch, getState, DeviceManager) => {
const state = getState();
console.log("READ TEMP");
dispatch(temperatureSensor( Math.random(0,9) ))
}
}
function BLEservices(BLEServices) {
setInterval(() => {
BLEServices.readTemp();
}, 2500);
return (
<SafeAreaView style={styles.container}>
<Slider
childrenContainer={{ }}
onEndReached={() => {
console.log('REACHED')
}}
containerStyle={{
height:40,
margin: 8,
backgroundColor: "#EEEEEE",
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
width: '50%',
}}
sliderElement={
<Text style={{color:"#FFF"}}>TEST</Text>
}
>
<Text style={{color: "#D5BD9E"}}>unlock</Text>
</Slider>
<Text>Temperature: {BLEServices.temperatureSensor}</Text>
</SafeAreaView>
);
}
thanks for your advice, and your help
Update 2
Solution found, see my answer below. The problem was type of var user in dispatch and some side effect due to previous test I have done on app and not clean them.
I solved my problem, by finding multiple var who are contains objects. I have a var which contain four attributes, I update and use one of them. And this object was update by my watcher. When I dispatch object to get a part of this object, I have to read the whole object, and this one is fully updated by my watchern which cause laggy render. So i have splitted that, to update only per var.
Another thing I've done, I split my interface elements in multi component, before, I has a lot of code in one screen, because I didn't need to reuse them elsewhere.
I was wondering, is it possible to add a react component as the content?
I added the component inside the overlay like so -
this.player.overlay({
content: <SomeReactComponent />,
align: 'bottom-left',
overlays: [{
start: 'play',
end: 'end'
}]
});
and the SomeReactComponent is just a react component for a dynamic image renderer that looks like this
import like from './like.png';
import love from './love.png';
import neutral from './neutral.png';
class SomeReactComponent extends Component {
getImage(pic) {
const image = pic;
return image;
}
render() {
const pic = [love, like, neutral];
return (
<div>
{ sentimentsArray.map(sentiment =>
<img src={this.getImage(pic)} style={{ width: '75%', height: '75%', objectFit: 'scale-down' }} />
)}
</div>
);
}
}
When i call this.player.overlay in my console, it says the overlays.options.content is a Symbol of React.element, however, I'm not getting anything as an overlay
It's not possible to use React component for this property unfortunately, but only string or node element. Take a look to the doc for more information.
I am trying to type annotate my React Native project using Flow, but I am having trouble finding the type definitions for the Text and TouchableOpacity elements so I can reference their style prop type definitions. How can I import and/or reference the type definitions for these elements?
Code below:
// #flow
import * as React from "react";
import { Text, TouchableOpacity } from "react-native";
type Props = {
segmentIndex: number,
segmentInfo: {
title: string
},
segmentStyle: ???, // WHAT SHOULD I USE HERE?
titleStyle: ???, // WHAT SHOULD I USE HERE?
onSelection: (number) => void
}
export const SegmentButton = (props: Props) => {
const _segmentPressed = () => {
props.onSelection(props.segmentIndex)
}
return (
<TouchableOpacity style={props.segmentStyle} onPress={_segmentPressed}>
<Text style={props.titleStyle}>{props.segmentInfo.title.charAt(0).toUpperCase() + props.segmentInfo.title.substring(1)}</Text>
</TouchableOpacity>
)
}
There are many ways to define StyleSheet type.
1. react-native generic way
reference from react-native library,
SwipeableQuickActionButton.js
import { View } from 'react-native';
type Prop = {
style?: ?View.propTypes.style,
}
2. react-navigation generic way
reference from react-navigation library,
TypeDefinition.js
export type Style =
| { [key: string]: any }
| number
| false
| null
| void
| Array<Style>;
If you already installed react-navigation, import it from:
import type { Style } from 'react-navigation/src/TypeDefinition';
type Prop = {
style?: Style,
}
Or you can still use it by define in your own file.
3. make Style type specific for <Text /> and <TouchableOpacity />
It would be the hard way -- although doable, see if that worth the trouble.
take <Picker /> for reference, it defined itemStyle for <Text /> like below, which corresponding to text style for each picker item.
var StyleSheetPropType = require('StyleSheetPropType');
var TextStylePropTypes = require('TextStylePropTypes');
var TextStyle = StyleSheetPropType(TextStylePropTypes);
type Prop = {
itemStyle: TextStyle
}
To use it in your own library, import them from:
import TextStylePropTypes from 'react-native/Libraries/Text/TextStylePropTypes';
import StyleSheetPropType from 'react-native/Libraries/StyleSheet/StyleSheetPropType';
let TextStyle = StyleSheetPropType(TextStylePropTypes);
type Prop = {
style?: ?TextStyle,
}
I am writing a small ReactNative application that allows users to invite people to events.
The design includes a list of invitees, each of which is accompanied by a checkbox used to invite/uninvite said invitee. Another checkbox at the top of the list that performs a mass invite/uninvite on all invitees simultaneously. Finally a button will eventually be used to send out the invites.
Because the state of each of these elements depends changes made by the other I often need to re-render my entire UI whenever the user takes action on one of them. But while this works correctly it is causing me quite a few performance issues, as shown in this video
Here's the code I'm using:
import React, { Component } from 'react';
import { Container, Header, Title,
Content, Footer, FooterTab,
Button, Left, Right,
Center, Body, Text, Spinner, Toast, Root , CheckBox, ListItem, Thumbnail} from 'native-base';
import { FlatList, View } from 'react-native';
export default class EventInviteComponent extends Component {
constructor(props) {
super(props);
console.disableYellowBox = true;
this.state = {
eventName: "Cool Outing!",
invitees:[]
}
for(i = 0; i < 50; i++){
this.state.invitees[i] = {
name: "Peter the " + i + "th",
isSelected: false,
thumbnailUrl: 'https://is1-ssl.mzstatic.com/image/thumb/Purple111/v4/62/08/7e/62087ed8-5016-3ed0-ca33-50d33a5d8497/source/512x512bb.jpg'
}
}
this.toggelSelectAll = this.toggelSelectAll.bind(this)
}
toggelSelectAll(){
let invitees = [...this.state.invitees].slice();
let shouldInviteAll = invitees.filter(invitee => !invitee.isSelected).length != 0
let newState = this.state;
newState = invitees.map(function(invitee){
invitee.isSelected = shouldInviteAll;
return invitee;
});
this.setState(newState);
}
render() {
let invitees = [...this.state.invitees];
return (
<Root>
<Container>
<Content>
<Text>{this.state.eventName}</Text>
<View style={{flexDirection: 'row', height: 50, marginLeft:10, marginTop:20}}>
<CheckBox
checked={this.state.invitees.filter(invitee => !invitee.isSelected).length == 0}
onPress={this.toggelSelectAll}/>
<Text style={{marginLeft:30 }}>Select/deselect all</Text>
</View>
<FlatList
keyExtractor={(invitee, index) => invitee.name}
data={invitees}
renderItem={(item)=>
<ListItem avatar style={{paddingTop: 20}}>
<Left>
<Thumbnail source={{ uri: item.item.thumbnailUrl}} />
</Left>
<Body>
<Text>{item.item.name}</Text>
<Text note> </Text>
</Body>
<Right>
<CheckBox
checked={item.item.isSelected}/>
</Right>
</ListItem>}/>
</Content>
<Footer>
<FooterTab>
<Button full
active={invitees.filter(invitee => invitee.isSelected).length > 0}>
<Text>Invite!</Text>
</Button>
</FooterTab>
</Footer>
</Container>
</Root>);
}
}
In your code, in class method toggelSelectAll() {...} you modify the state directly by using this.state = ..., which is something to be avoided. Only use this.state = ... in your class constructor() {...} to initialize the state, and you should only use this.setState({...}) to update the state anywhere else.
Not sure if this should help your performance issues, but try replacing toggelSelectAll() with the following:
toggelSelectAll() {
const {invitees} = this.state;
const areAllSelectedAlready = invitees.filter(({isSelected}) => !isSelected).length === 0;
this.setState({
invitees: invitees.map(invitee => ({
...invitee,
isSelected: !areAllSelectedAlready
}))
});
}
Good luck! And, let me know if you would like me to refactor your above code to remove the 2nd this.state = ... in your constructor (which, once again, should be avoided when writing React).
I suggest:
Dividing your code by creating multiple components, so you won't have a massive render()
Using Redux to store invitee / global state, so you can choose which components should re-render in case of modifications
That's a good way to learn React Native!