A problem with react hooks with animation callback - react-native

import React, {useEffect, useState} from 'react';
import {Col} from "native-base";
import {Animated, TouchableOpacity, ViewProps} from "react-native";
interface AnimatedButtonProps extends ViewProps {
text: string;
size: Animated.Value;
onPress: (cb?: () => void) => void;
}
export const AnimatedButton = ({text, size, onPress}: AnimatedButtonProps) => {
const [defaultSize, setDefaultSize] = useState(new Animated.Value(30));
useEffect(() => {
// Update defaultSize from props
setDefaultSize(size);
});
let _onPress = () => {
console.log(defaultSize);
Animated.timing(defaultSize, {
toValue: 50,
duration: 300,
}).start();
console.log(defaultSize);
};
return (
<Col style={{justifyContent: "center", alignItems: "center"}}>
<TouchableOpacity style={{
width: 60,
height: 60,
justifyContent: "center",
alignItems: "center",
}}
onPress={() => onPress(_onPress)}>
<Animated.Text style={{
fontSize: defaultSize,
fontWeight: "bold"
}}>{text}</Animated.Text>
</TouchableOpacity>
</Col>
)
};
I am new to react hooks, tried to rewrite one of my components with react hooks. Any one can tell me why the callback animation doesn't work? The call back did trigger, but the defaultSize doesn't change at all. Below is my original component wrote in "React Class" way which works as expected. Some help will be really appreciated :D
class AnimatedButton extends Component<AnimatedButtonProps, AnimatedButtonState> {
state: AnimatedButtonState = initState;
componentDidMount(): void {
const {size} = this.props;
this.setState({
size
})
}
_onPress = () => {
const {size} = this.state;
Animated.sequence([
Animated.timing(size, {
toValue: 50,
duration: 300,
}),
Animated.timing(size, {
toValue: 30,
duration: 300,
})
]).start();
};
render() {
const {text} = this.props;
const {size} = this.state;
return (
<Col style={{justifyContent: "center", alignItems: "center"}}>
<TouchableOpacity style={{
width: 60,
height: 60,
justifyContent: "center",
alignItems: "center",
}}
onPress={() => this.props.onPress(this._onPress)}>
<Animated.Text style={{
fontSize: size,
fontWeight: "bold"
}}>{text}</Animated.Text>
</TouchableOpacity>
</Col>
);
}
}
export default AnimatedButton;

useEffect(() => {
setDefaultSize(size);
}, [defaultSize]);
Solved the problem

Related

Unable to access state variable declared inside useEffect

I have a react native screen where I am calling an API on useEffect and setting two state variables using hooks, i.e. cityList, filteredCityList. filteredCityList is bind to autocomplete dropdown. On change of this dropdown, a function called filterCity gets called. I am unable to access the cityList inside this function.
Please advise what am I missing here.
import React, {useCallback, useEffect, useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {Button, Card, Portal, Text, Modal, FAB} from 'react-native-paper';
import {AutocompleteDropdown} from 'react-native-autocomplete-dropdown';
import Icon from 'react-native-vector-icons/Feather';
import {getCities} from '../API/api';
import {useFocusEffect} from '#react-navigation/native';
const AddHospital = () => {
const [selectedCity, setSelectedCity] = useState(null);
const [cityListFiltered, setCityListFiltered] = useState([]);
const [cityList, setCityList] = useState([]);
const [cityDDLoading, setCityDDLoading] = useState(false);
const [selectedCityError, setSelectedCityError] = useState(false);
console.log('reloading');
const filterCity = q => {
console.log(q);
console.log('City List', cityList);
};
const loadCities = async () => {
setCityDDLoading(true);
var citiesResponse = await getCities();
if (citiesResponse.responseData) {
var cities = citiesResponse.responseData.records.map(item => ({
id: item.id,
title: item.cityname,
}));
console.log('setting cities', cities);
setCityList(cities);
// setCityListFiltered(cities);
// setCityDDLoading(false);
} else {
console.log('Failed to load cities');
}
};
useEffect(() => {
loadCities();
}, []);
return (
<View>
<Card style={styles.container}>
<Card.Title title="Select Hospital"></Card.Title>
<Card.Content>
<AutocompleteDropdown
clearOnFocus={false}
closeOnBlur={true}
closeOnSubmit={false}
onSelectItem={item => {
item && setSelectedCity(item);
}}
dataSet={cityListFiltered}
debounce={600}
onChangeText={text => filterCity(text)}
suggestionsListMaxHeight={120}
loading={cityDDLoading}
useFilter={false} // prevent rerender twice
textInputProps={{
placeholder: 'Select City',
autoCorrect: false,
autoCapitalize: 'none',
style: {
// borderRadius: 25,
backgroundColor: '#f6f6f6',
color: 'black',
paddingLeft: 18,
borderWidth: 1,
borderColor: selectedCityError ? 'red' : 'gray',
borderStyle: 'solid',
},
placeholderTextColor: 'black',
}}
rightButtonsContainerStyle={{
// borderRadius: 25,
right: 8,
height: 30,
top: 10,
alignSelfs: 'center',
backgroundColor: '#f6f6f6',
// backgroundColor: "#383b42"
}}
inputContainerStyle={{
backgroundColor: 'transparent',
zIndex: 0,
}}
suggestionsListContainerStyle={
{
//backgroundColor: "#383b42"
//color: '#fff'
// zIndex: 1000
}
}
containerStyle={{
flexGrow: 1,
flexShrink: 1,
marginBottom: 100,
marginTop: 10,
zIndex: 0,
}}
renderItem={(item, text) => (
<Text style={{color: 'black', padding: 15}}>{item.title}</Text>
)}
ChevronIconComponent={<Icon name="chevron-down" size={24} />}
ClearIconComponent={<Icon name="x" size={18} />}
inputHeight={50}
showChevron={true}
showClear={true}
// style={styles.autoComplete}
/>
</Card.Content>
</Card>
<Text>{cityList.length}</Text>
</View>
);
};
export default AddHospital;
const styles = StyleSheet.create({
container: {
flex: 0,
margin: 10,
},
});

React Native app only works after saving file twice

Basically, a request is made to an API and then the expected result is only presented on the screen after saving the file in vscode twice.
The API is returning the correct result the first time, the problem seems to be linked to useEffect and the updating of the components on the screen, because when I update the desirable result is presented.
I'm saving API data in AsyncStorage and then recovering. I have no idea if I'm doing it right or if there's a better way, but the point is that the app doesn't update the components the first time.
user.js (context)
import React, { createContext, useEffect, useState, useContext } from "react"
import AsyncStorage from "#react-native-async-storage/async-storage"
import ApiRequest from '../services/Api';
const UserContext = createContext({})
export const UserProvider = ({ children }) => {
const [user, setUser] = useState({})
const [services, setServices] = useState([])
useEffect(() => {
let isSubscribed = true;
const getStorage = async (isSubscribed) => {
const storagedToken = await AsyncStorage.getItem('token');
const storagedId = await AsyncStorage.getItem('id');
if(storagedToken && storagedId) {
isSubscribed ? getUser(storagedToken, storagedId) : null;
}
}
getStorage(isSubscribed);
return () => (isSubscribed = false);
}, [])
async function getUser(storagedToken, storagedId) {
try {
const resp = await ApiRequest.getUser(storagedToken, storagedId)
let user = JSON.stringify(resp.Cadastro)
await AsyncStorage.setItem('user', user);
} catch (error) {
console.log('Não foi possível recuperar os dados do usuário, tente novamente.')
}
}
return (
<UserContext.Provider value={{ user, currentService, services, setServiceDate, setServiceType, setCurrentService }}>
{children}
</UserContext.Provider>
)
}
export const useUser = () => {
return useContext(UserContext)
}
Api.js
import axios from 'axios';
import UrlGenerator from './UrlGenerator';
const ApiRequest = {
async getUser(token, id){
try {
const uri = await UrlGenerator.user();
const resp = await axios.post(uri, {
Token: token,
cadastroKey: id
});
return resp.data;
} catch (error) {
//setState(error.toString());
console.log(error);
}
}
}
export default ApiRequest;
WelcomeChamado.js
import React, { useEffect, useState } from 'react'
import { View, Image, ScrollView, StyleSheet, TouchableOpacity } from 'react-native'
import { LinearGradient } from 'expo-linear-gradient';
import { initialWindowMetrics, SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
import NavBar from '../../../components/NavBar';
import { TextMuseo300, TextMuseo500 } from '../../../components/fonts/TextFonts';
import { theme } from '../../../global/theme';
import { TextInputMuseo300 } from '../../../components/fonts/TextInputFonts';
import AsyncStorage from '#react-native-async-storage/async-storage';
const { principalOne, principalTwo, principalThree, destaque, branco } = theme.colors;
import { Entypo } from '#expo/vector-icons';
import loadStoragedUser from '../../../helpers/getUser';
export function WelcomeChamado({ navigation }) {
const insets = useSafeAreaInsets();
const [user, setUser] = useState({});
const [foto, setFoto] = useState('');
useEffect(() => {
async function getUser() {
setUser(await loadStoragedUser());
}
const getInfo = async () => {
const storagedFoto = await AsyncStorage.getItem('#lica:foto');
setFoto(storagedFoto);
}
getInfo();
getUser();
}, []);
const nextScreen = () => {
navigation.navigate("AguardarChamado");
}
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{
flexGrow: 1,
}}>
<SafeAreaProvider initialMetrics={initialWindowMetrics}
style={
styles.container,
{
paddingTop: insets.top,
paddingLeft: insets.left,
paddingBottom: insets.bottom,
paddingRight: insets.right,
}
}>
<LinearGradient
start={{ x: 0, y: 0.5 }}
end={{ x: 1, y: 0.5 }}
locations={[0, 0.2, 0.4, 0.6, 0.8, 1]}
colors={['#8EF4F0', '#D6FDFA', '#F8FEFE', '#F8FEFE', '#D6FDFA', '#8EF4F0']}
style={{
flex: 1
}}>
<NavBar navigation={navigation} />
<View style={{
alignItems: 'center',
marginTop: '5%'
}}>
<TextMuseo300 style={{
color: '#2E8F71',
marginTop: '1%',
fontWeight: 'bold',
marginBottom: 10
}}>Olá</TextMuseo300>
<View style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={{ uri: `data:image/gif;base64,${foto}` }}
style={{
width: 100,
height: 100,
borderRadius: 50,
borderWidth: 1,
borderColor: destaque,
}}
resizeMode="cover"
/>
<View style={{
position: 'absolute',
top: -5,
right: 0,
backgroundColor: '#2E8F71',
width: 32,
height: 32,
borderRadius: 16,
paddingLeft: 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<Entypo
name="bell"
size={25}
color="#FFF"
/>
</View>
</View>
<TextMuseo300 style={{
color: '#2E8F71',
marginTop: '1%',
fontWeight: 'bold',
marginTop: 10
}}>{user?.CadastroNome}</TextMuseo300>
<TextMuseo300 style={{
color: '#2E8F71',
marginTop: '1%'
}}>{user?.CadastroUser}</TextMuseo300>
</View>
<View style={{
marginTop: 30,
alignItems: 'center',
}}>
<TextMuseo300 style={{
color: '#2E8F71',
marginTop: '2%',
maxWidth: 120,
fontSize: 20,
textAlign: 'center'
}}>Você está disponível para receber chamados?</TextMuseo300>
<TouchableOpacity onPress={nextScreen}>
<View style={{
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#2E8F71',
borderRadius: 10,
width: 120,
height: 30,
marginTop: 50
}}>
<TextMuseo300 style={{
color: '#FFF',
marginTop: '2%',
maxWidth: 120,
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center'
}}>Sim, estou.</TextMuseo300>
</View>
</TouchableOpacity>
</View>
</LinearGradient>
</SafeAreaProvider>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
center: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});

Issue with React Native animations

I'm facing a problem with centring the text after the animation finishes as you can see in the video here https://www.youtube.com/watch?v=hhBGUp9_GAY&feature=youtu.be. I want to get both titles perfectly centered horizontally on all devices no matter the screen width. I'm using the Animated API. Any suggestions?
Here is my approach
import React, { useEffect } from "react";
import { View, StyleSheet, Animated, Text, Dimensions, AsyncStorage } from "react-native";
export default function Welcome({ navigation }) {
const width = Dimensions.get('screen').width
let position1 = new Animated.ValueXY(0, 0);
let position2 = new Animated.ValueXY(0, 0);
useEffect(() => {
Animated.timing(position1, {
toValue: { x: width / 4.5, y: 0 },
duration: 900
}).start();
Animated.timing(position2, {
toValue: { x: -width / 3, y: 0 },
duration: 900
}).start();
}, []);
_retrieveData = async () => {
try {
const token = await AsyncStorage.getItem('tokehhn');
if (token !== null) {
// We have data!!
setTimeout(() => navigation.navigate('Home'), 2000)
} else {
setTimeout(() => navigation.navigate('Auth'), 2000)
}
} catch (error) {
// Error retrieving data
}
};
useEffect(() => {
_retrieveData()
}, [])
return (
<View style={styles.container}>
<Animated.View style={position1.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', fontWeight: 'bold', fontSize: 24, color: '#5790f9' }}>Welcome to Glue</Text>
</Animated.View>
<Animated.View style={position2.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', right: -220, fontWeight: 'bold', fontSize: 21, color: '#5790f9' }}>Where everything happens</Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
}
});
Thats how you do it:
let {width} = Dimensions.get('window')
export default function App() {
let animation = new Animated.Value(-width);
let translateX = animation.interpolate({inputRange:[-width,0],outputRange:[2*width,0]});
React.useEffect(()=>{
Animated.timing(animation,{toValue:0}).start();
},[])//eslint-ignore-line
return (
<View style={styles.container}>
<Animated.Text style={[styles.text,{transform:[{translateX:animation}]}]}>LOL</Animated.Text>
<Animated.Text style={[styles.text,{transform:[{translateX}]}]}>Longer LOLLLL</Animated.Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
text:{
textAlign:'center'
}
});
I have created snack as well
Make it a simple and clean interpolation.
The code looks always clean, and readable if we use Animated.Value in range of 0 - 1.
Full code:
import React, {useEffect} from 'react';
import {View, StyleSheet, Animated} from 'react-native';
const App = () => {
const animate = new Animated.Value(0);
const inputRange = [0, 1];
const translate1 = animate.interpolate({inputRange, outputRange: [-100, 0]});
const translate2 = animate.interpolate({inputRange, outputRange: [100, 0]});
useEffect(() => {
Animated.timing(animate, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<View style={styles.container}>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate1}]}]}>
First Text
</Animated.Text>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate2}]}]}>
Second Text
</Animated.Text>
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 25,
},
});
Using that animated value, implement any other animations if needed.
For example, If you need to scale the text while moving:
const scale = animate.interpolate({inputRange, outputRange: [1, 1.5]});

React native search onChange input text, code structure

I am building a searchBar, whenever I do search I get undefined error because the value doesn't exist in state till I finish the whole value so I know that I will get error yet I am unable to solve it so I am trying to render cards according to the search input I think I did hard code my homeScreen I am not sure if I am doing it even right and here it comes the question to the three if statements inside render that I have is it good practice ? is it professional ? can i do something else which makes code easier to read and shorter ? I was thinking of eliminating the third if but I wasn't able to change state inside the second if so I had to add the toggle search function to let it work any ideas on how to eliminate the third if would be nice ..! thank you in advance guys
homeScreen.js
import axios from 'axios';
import React from 'react';
import {
ActivityIndicator,
ScrollView,
Text,
View,
TouchableOpacity,
TextInput,
} from 'react-native';
import Card from '../Components/Card/card';
export default class HomeScreen extends React.Component {
state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
componentDidMount() {
this.getData();
}
toggleSearch = () => {
console.log('hlelleloe');
this.setState({
search: true,
});
};
getData = () => {
const requestUrls = Array.from({length: 9}).map(
(_, idx) => `http://api.tvmaze.com/shows/${idx + 1}`,
);
const handleResponse = data => {
this.setState({
isLoading: false,
shows: data,
});
};
const handleError = error => {
console.log(error);
this.setState({
isLoading: false,
});
};
Promise.all(requestUrls.map(url => axios.get(url)))
.then(handleResponse)
.catch(handleError);
};
render() {
const {isLoading, shows, search, title} = this.state;
if (isLoading) {
return <ActivityIndicator size="large" color="#0000ff" />;
} else if (!search) {
return (
<View>
<View>
<TouchableOpacity
onPress={this.toggleSearch}
style={{height: 300, width: 300}}>
<Text style={{textAlign: 'center', fontSize: 40}}>
Press to Search
</Text>
</TouchableOpacity>
</View>
<ScrollView style={{backgroundColor: '#E1E8E7'}}>
{shows.length &&
shows.map((show, index) => {
return (
<Card
key={show.data.id}
title={show.data.name}
rating={show.data.rating.average}
source={show.data.image.medium}
genres={show.data.genres}
language={show.data.language}
network={show.data.network}
schedule={show.data.schedule}
summary={show.data.summary}
navigation={this.props.navigation}
/>
);
})}
</ScrollView>
</View>
);
} else if (search) {
console.log(title);
return (
<View>
<TextInput
style={{
height: 100,
width: 100,
borderColor: 'gray',
borderWidth: 1,
}}
onChangeText={searchedTitle => (
<Card title={shows.data.searchedTitle} />
)}
/>
</View>
);
}
}
}
Card.js
import React from 'react';
import {
Image,
View,
Text,
Button,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from 'react-native-responsive-screen';
import Icon from 'react-native-vector-icons/FontAwesome';
const Card = props => {
return (
<View style={styles.container}>
<Image style={styles.Image} source={{uri: `${props.source}`}} />
<Text style={styles.title}>{props.title}</Text>
<View style={styles.ratingContainer}>
<Text style={styles.rating}>Rating: {props.rating}</Text>
<Icon name="star" size={30} color="grey" />
</View>
<TouchableOpacity
style={styles.button}
onPress={() => {
props.navigation.navigate('Details', {
title: props.title,
rating: props.rating,
source: props.source,
genres: props.genres,
language: props.language,
network: props.network,
schedule: props.schedule,
summary: props.summary,
});
}}>
<Text style={styles.buttonText}>Press for details </Text>
</TouchableOpacity>
</View>
);
};
export default Card;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
Image: {
flex: -1,
width: wp('90%'),
height: hp('65%'),
},
title: {
flex: 1,
fontSize: 40,
borderRadius: 10,
color: '#3C948B',
margin: 15,
justifyContent: 'center',
alignItems: 'center',
},
ratingContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'white',
elevation: 6,
justifyContent: 'space-between',
borderWidth: 1,
width: 300,
},
rating: {
fontSize: 25,
paddingLeft: 15,
},
button: {
flex: 1,
color: '#3C948B',
backgroundColor: '#3C948B',
height: hp('7%'),
width: wp('70%'),
margin: 20,
alignItems: 'center',
borderBottomLeftRadius: 10,
borderTopRightRadius: 10,
},
buttonText: {
flex: 1,
fontSize: 25,
},
});
you Need to implement a constructor for your React component.
Typically, in React constructors are only used for two purposes:
Initializing local state by assigning an object to this.state
Binding event handler methods to an instance
Do
state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
replace this with
constructor(props){
super(props);
this.state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
}

Connect() not working ( no error ) in Redux React Native app

I'm trying to create a small counting number app, all it does is when I click increase, the counter value increase.
And I'm trying to apply Redux to it, but it not working.
Because there is no error thrown, I really don't know where to fix. Please help.
Thank you in advance!
I checked the store.getState() and the appReducer , it worked just fine. I think the problem is that I did something wrong and the connect() didn't work probably.
/* STORE */
import { createStore } from 'redux';
const INCREASE = 'increase';
const DECREASE = 'decrease';
const increase = () => { type: INCREASE }
const decrease = () => { type: DECREASE }
const initialState = { count: 0 };
function appReducer(state = initialState, action) {
switch (action.type) {
case INCREASE:
return { count: state.count + 1 };
case DECREASE:
return { count: state.count - 1 };
}
return state;
}
const store = createStore(appReducer);
/* COMPONENT */
export class Main extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={{ flex: 1 }}>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#4a99f9',
}}>
<Text
style={{
color: 'white',
fontSize: 100,
fontWeight: 'bold',
textAlign: 'center',
}}>
{ this.props.count }
</Text>
</View>
<View
style={{
flex: 1,
padding: 30,
alignItems: 'center',
backgroundColor: '#fff711',
}}>
<TouchableOpacity
style={{
margin: 5,
width: 200,
height: 50,
backgroundColor: '#51f772',
justifyContent: 'center',
}}
onPress={increase}>
<Text
style={{
color: 'white',
textAlign: 'center',
fontWeight: 'bold',
}}>
Increase
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
margin: 5,
width: 200,
height: 50,
backgroundColor: '#ff5c38',
justifyContent: 'center',
}}
onPress={decrease}>
<Text
style={{
color: 'white',
textAlign: 'center',
fontWeight: 'bold',
}}>
Decrease
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
/* ROOT */
import { connect } from 'react-redux';
const mapStateToProps = state => { count: state.count };
const mapDispatchToProps = dispatch => {
return {
increase: () => dispatch(increase()) ,
decrease: () => dispatch(decrease())
}
};
const AppContainer = connect(mapStateToProps , mapDispatchToProps)(Main);
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { Provider } from 'react-redux';
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
}
}
Hi change your component file to below code and rerun. You forgot to dispatch your actions.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import { connect } from 'react-redux'
class CounterApp extends Component {
render() {
return (
<View style={styles.container}>
<View style={{ flexDirection: 'row', width: 200, justifyContent: 'space-around' }}>
<TouchableOpacity onPress={() => this.props.increaseCounter()}>
<Text style={{ fontSize: 20 }}>Increase</Text>
</TouchableOpacity>
<Text style={{ fontSize: 20 }}>{this.props.counter}</Text>
<TouchableOpacity onPress={() => this.props.decreaseCounter()}>
<Text style={{ fontSize: 20 }}>Decrease</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
function mapStateToProps(state) {
return {
counter: state.counter
}
}
function mapDispatchToProps(dispatch) {
return {
increaseCounter: () => dispatch({ type: 'INCREASE' }),
decreaseCounter: () => dispatch({ type: 'DECREASE' }),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CounterApp)
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});