I'm using this snack as a template for implementing my own carousel component. This template is using Class Component and I converted it to a Functional component
https://snack.expo.io/#bd-arc/react-native-snap-carousel-%7C-example-with-custom-interpolations
I added this to my project and I believe some error is happening when rendering the Carousel but I'm unable to tell. It renders - but not all the components are rendering. I have only changed the Carousel itself to a function component. The problem is the is not showing below the Carousel - also if I add multiple VideoCarousel's, only the first one displays.
My code is below
App.js
import React from 'react';
import { View } from 'react-native';
import Header from './components/header';
import VideoCarousel from './components/videoCarousel';
import tailwind from 'tailwind-rn';
const App = () => {
return (
<View style={tailwind('flex pt-12 items-center bg-gray-300 h-full')}>
<Header />
<VideoCarousel />
<VideoCarousel />
</View>
)
};
export default App;
VideoCarousel.js
import React, { useState } from 'react';
import { Text, View, Dimensions, StyleSheet, Alert } from 'react-native';
import Carousel from 'react-native-snap-carousel'; // Version can be specified in package.json
import { scrollInterpolator, animatedStyles } from './../utils/animations';
const SLIDER_WIDTH = Dimensions.get('window').width;
const ITEM_WIDTH = Math.round(SLIDER_WIDTH * 0.7);
const ITEM_HEIGHT = Math.round(ITEM_WIDTH * 3 / 4);
const DATA = [];
for (let i = 0; i < 10; i++) {
DATA.push(i)
}
const VideoCarousel = () => {
const [index, setIndex] = useState(0);
_renderItem = (index) => {
return (
<View style={styles.itemContainer}>
<Text style={styles.itemLabel}>{`Item ${index.index}`}</Text>
</View>
);
}
return (
<View>
<Carousel
data={DATA}
renderItem={this._renderItem}
sliderWidth={SLIDER_WIDTH}
itemWidth={ITEM_WIDTH}
containerCustomStyle={styles.carouselContainer}
inactiveSlideShift={0}
onSnapToItem={(index) => setIndex(index)}
scrollInterpolator={scrollInterpolator}
slideInterpolatedStyle={animatedStyles}
useScrollView={true}
/>
<Text style={styles.counter}>
Test
</Text>
</View>
);
}
const styles = StyleSheet.create({
carouselContainer: {
marginTop: 50
},
itemContainer: {
width: ITEM_WIDTH,
height: ITEM_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'dodgerblue'
},
itemLabel: {
color: 'white',
fontSize: 24
},
counter: {
marginTop: 400,
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
backgroundColor: "black",
}
});
export default VideoCarousel;
Related
I'm trying to apply layout animation to a FlatList upon adding and deleting a goal (list item) using the Reanimated API. I'm mainly following this tutorial from the Reanimated docs but I don't know why the animation is not applied when list items are added or removed. I should also inform that I'm only testing this on an Android device. Here is the code:
App.js (contains FlatList)
import { useState } from "react";
import { Button, FlatList, StyleSheet, View } from "react-native";
import GoalInput from "./components/GoalInput";
import GoalItem from "./components/GoalItem";
export default function App() {
const [goalList, setGoalList] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const styles = StyleSheet.create({
appContainer: {
paddingTop: 50,
},
});
function startAddGoalHandler() {
setIsModalOpen(true);
}
// spread existing goals and add new goal
function addGoalHandler(enteredGoalText) {
setGoalList((currentGoals) => [
...currentGoals,
{ text: enteredGoalText, id: Math.random().toString() },
]);
}
function deleteGoalHandler(id) {
setGoalList((currentGoals) =>
currentGoals.filter((existingGoals) => existingGoals.id !== id)
);
}
return (
<View style={styles.appContainer}>
<Button
title='Add New Goal'
color='indigo'
onPress={startAddGoalHandler}
/>
{isModalOpen && (
<GoalInput
isModalOpen={isModalOpen}
setIsModalOpen={setIsModalOpen}
onAddGoal={addGoalHandler}
></GoalInput>
)}
<FlatList
keyExtractor={(item, index) => {
return item.id;
}}
data={goalList}
renderItem={(itemData) => {
return (
<GoalItem
onGoalDelete={deleteGoalHandler}
itemData={itemData}
/>
);
}}
/>
</View>
);
}
GoalItem.js (list item)
import React from "react";
import { Pressable, StyleSheet, Text } from "react-native";
import Animated, { Layout, LightSpeedInLeft, LightSpeedOutRight } from "react-native-reanimated";
const GoalItem = ({ itemData, onGoalDelete }) => {
const styles = StyleSheet.create({
goalCards: {
elevation: 20,
backgroundColor: "white",
shadowColor: "black",
height: 60,
marginHorizontal: 20,
marginVertical: 10,
borderRadius: 10,
},
});
return (
<Animated.View
style={styles.goalCards}
entering={LightSpeedInLeft}
exiting={LightSpeedOutRight}
layout={Layout.springify()}
>
<Pressable
style={{ padding: 20 }}
android_ripple={{ color: "#dddddd" }}
onPress={() => onGoalDelete(itemData.item.id)}
>
<Text style={{ textAlign: "center" }}>
{itemData.item.text}
</Text>
</Pressable>
</Animated.View>
);
};
export default GoalItem;
I've even tried replacing the FlatList with View but to no avail. I suspect that Reanimated isn't properly configured for my project, if I wrap some components with <Animated.View>...</Animated.View> (Animated from Reanimated and not the core react-native module) for example the child components will not show. Reanimated is installed through npm
Any help is appreciated, thanks!
I got an error called "ReferenceError: Can't find variable: SearchBar". I tried to check for syntax error and run the app again. But the same error appeared. How can I rectify it? Thanks.
This is my SearchScreen.js file.
import React, { useState } from 'react';
import { StyleSheet, View, TextInput } from 'react-native';
const SearchScreen = () => {
const [term, setTerm] = useState('')
return (
<View>
<SearchBar
term={term}
onTermChange={(newTerm) => setTerm(newTerm)} />
</View>
);
}
const styles = StyleSheet.create({});
export default SearchScreen;
This is my SearchBar.js file.
import React from 'react';
import { StyleSheet, View, TextInput } from 'react-native';
import { EvilIcons } from '#expo/vector-icons'
const SearchBar = ({ term, onTermChange }) => {
return (
<View style = {styles.background}>
<EvilIcons style={styles.icon} name="search" />
<TextInput
value={term}
style={styles.input}
placeholder="Search"
onChangeText={newTerm => onTermChange(newTerm)} />
</View>
);
}
const styles = StyleSheet.create({
background: {
height: 50,
borderRadius: 6,
marginHorizontal: 15,
flexDirection: 'row',
alignItems: 'center'
},
input: {
flex: 1
},
icon: {
fontSize: 35,
alignSelf: 'center',
marginHorizontal: 15
}
});
In my parent I have this code:
So I render inside it my custom inputs by this way:
My doubt is how I can access on any part of this parent the text of each input using the ref. Someone can help me?
The textinput component:
https://gist.github.com/ThallyssonKlein/4e054bc368ebc153fbf9222e304ff887
I couldn't solve the problem, apparently there is no way to get this property in pure React-Native.
So I started using the TextInput component of the react-native-paper package. This way the same code worked, I can get the text now with this excerpt:
console.log(refContainerStep1.current.state.value);
use useRef() instead of createRef();
const textInput = useRef(null);
<TextInput
ref={textInput}
....../>
You can access the ref via refContainerStep1.current.
What you can then do is check the Prototype property to check which methods you can use.
I noticed there's a function called _getText which can be used to obtain a value.
An example of grabbing the value in an onPress:
const onPress = () => {
console.log(refContainerStep1.current.__proto__); // See available methods
console.log(refContainerStep1.current._getText()); // Grab the value
}
Do it that way
const onButtonClick = () => {
console.log('get value from parent')
console.log(ref1.current.props.value)
console.log(ref2.current.props.value)
};
Example in expo
Parent
import * as React from 'react';
import { Text, View, StyleSheet,TextInput } from 'react-native';
import Constants from 'expo-constants';
import MyTextInput from './components/AssetExample';
import { Card } from 'react-native-paper';
export default function App() {
const ref1 = React.createRef();
const ref2 = React.createRef();
const onButtonClick = () => {
console.log(ref1.current.props.value)
console.log(ref2.current.props.value)
};
return (
<View style={styles.container}>
<Card>
<button onClick={onButtonClick}>get value</button>
<MyTextInput label={'label 2'} secure={false} ref={ref1} />
<MyTextInput label={'label 1'} secure={true} ref={ref2} />
</Card>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Child
import React, { useState, useEffect } from 'react';
import { TextInput as RnTextInput, StyleSheet, View, Text } from 'react-native';
const styles = StyleSheet.create({
textInput: {
padding: 10,
marginRight: 10,
marginLeft: 10,
borderRadius: 50,
},
text: {
marginLeft: 20,
marginBottom: 10,
fontSize: 20,
},
});
const TextInput = React.forwardRef((props, ref) => {
const [text, setText] = useState('');
return (
<View>
{props.label && <Text style={styles.text}>{props.label}</Text>}
<RnTextInput
style={styles.textInput}
value={text}
onChange={(e) => {
setText(e.target.value);
}}
secureTextEntry={props.secure}
ref={ref}
/>
</View>
);
});
export default TextInput;
I am trying to display a simple .jpg image by sending the Image path as a prop to the component which is supposed to render it. In the below way.
App.js
import React, { Component } from 'react';
import { View, Text, Image } from 'react-native';
import Header from './components/Header';
import ImageSlider from './components/ImageSlider';
import ImageShow from './components/ImageShow';
class App extends Component {
render () {
return (
<View style={{flex:1}}>
<Header headerText = "HONEST REVIEWS" />
<ImageSlider />
<ImageShow imagePath = "./abc.jpg"/>
<ImageShow imagePath = "./abc.png" />
</View>
);
}
}
export default App;
ImageShow.js
import React from 'react';
import { View, Image, Dimensions } from 'react-native';
const widthOfScreen = Dimensions.get('window').width;
const ImageShow = (imageProps) => {
return (
<View>
<Image style = { {width: 50, height: 50} } source = { {uri: imageProps.imagePath} } />
</View>
);
};
const styles = {
ImageStyle : {
height: 30,
width: widthOfScreen
}
}
export default ImageShow;
ImageSlider.js
import React from 'react';
import Carousel from 'react-native-banner-carousel';
import { StyleSheet, Image, View, Dimensions } from 'react-native';
const BannerWidth = Dimensions.get('window').width;
const BannerHeight = 250;
const images = [
require('./abc.jpg'),
require('./abc.jpg'),
require('./abc.jpg')
];
export default class ImageSlider extends React.Component {
renderPage(image, index) {
return (
<View key={index}>
<Image style={styles.imagesInSlider} source = { image } />
</View>
);
}
render() {
return (
<View style={styles.container}>
<Carousel
autoplay
autoplayTimeout={2000}
loop
index={0}
pageSize={BannerWidth}
>
{images.map((image, index) => this.renderPage(image, index))}
</Carousel>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
justifyContent: 'flex-start',
alignItems: 'center'
},
imagesInSlider: {
width: BannerWidth,
height: 250,
resizeMode: 'stretch',
}
});
My folder structure is :
ProjectName
------src
--------components
-----------ImageShow.js
-----------ImageSlider.js
-----------Header.js
-----------abc.jpg
--------App.js
Ideally the Image should be displayed when I am passing the locally stored Image path, but I am not getting any Image displayed but a Warning message which says:
"failed prop type: invalid prop 'source' supplied to 'ForwardRef(image)'"
This code work for me to get the image
import image1 from '../assets/images/abc.png'
import image2 from '../assets/images/abc.png'
const images = [
{
name: image1
},
{
name: image2
}]
const RenderPage = ({ image }) => {
return (
<Image resizeMode="contain" style={{ width: 50, height: 100, marginBottom: 5 }} source={image} />
)
}
class myclass extends Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{images.map((image, index) =>
<RenderPage key={index} image={image.name} />
)}
</View>
)
}
}
Here the screenshot:
I'm doing this course at Udemy. Files: https://github.com/StephenGrider/ReactNativeReduxCasts/tree/master/auth
The issue I'm having is with importing common components. It's only for 2 of the common components--the rest work fine. Card, CardSection, Header, Input.
When I try to import the Button or Spinner, they won't fire. But if I use the basic functionality (putting the TouchableOpacity or ActivityIndicator in the file directly and do all the styling THERE), they work fine.
Here's the file structure:
Here's /components/common/index.js
export * from './Header';
export * from './Input';
export * from './Card';
export * from './CardSection';
export * from './Button';
export * from './Spinner';
Here's Button.js
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ propOnPress, children }) => {
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={propOnPress} style={buttonStyle}>
<Text style={textStyle}>
{children}
</Text>
</TouchableOpacity>
)
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#fff',//'#007aff',
fontSize: 16,
fontWeight: '600',
paddingTop: 10,
paddingBottom: 10
},
buttonStyle: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#007aff', //'#fff',
borderRadius: 5,
borderWidth: 1,
borderColor: '#007aff',
marginLeft: 5,
marginRight: 5,
}
}
export { Button };
Here's Spinner.js
import React from 'react';
import { View, ActivityIndicator } from 'react-native';
const Spinner = ({ size }) => {
return (
<View style={styles.spinnerStyle}>
<ActivityIndicator size={size || 'large'} />
</View>
)
}
const styles = {
spinnerStyle: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
}
export { Spinner }
Here's where I import them in LoginForm.js
import { Card, CardSection, Button, Spinner, Input } from './common';
And where they're used in the code in LoginForm.js
renderButton() {
//console.log('render button');
if (this.state.loading) {
console.log('returning the spinner');
return <Spinner animating={this.state.loading} size="small" />;
}
console.log('gonna return a button');
return(
<Button onPress={this.onYouPressedIt.bind(this)}>Log in</Button>
);
}
What am I doing wrong?
Issues I see with the Button.js file
Property onPress doesn't exist. You created a property called propOnPress
So, your Button component should be used like this
<Button propOnPress={}>...</Button>
Issues I see with the Spinner.js file
Property animating doesn't exist on the component. The only properties you created is size.
Solution would be to simply add an animating property to your Spinner component.
Component would end up looking like this
const Spinner = ({ animating, size }) => {
return (
{
animating ? (
<View style={styles.spinnerStyle}>
<ActivityIndicator size={size || 'large'} />
</View>
) : null
}
)
}
I assume the animating property is a boolean which if false then you don't want to display the activity indicator which is why I added the ternary operator.