I was following this tutorial on Linkedin-Learning and suddelny my code wouldn't work anymore.
Unfortunately i don't really understand these error messages:
This warning always appears:
Warning: Failed prop type: The prop onPress is marked as required in
RandomNumber, but its value is undefined.
And if I press one of the "buttons", this error messages comes up:
TypeError: _this.props.onPress is not a function. (In
'_this.props.onPress(_this.props.id)', '_this.props.onPress' is
undefined)
RandomNumber.js:
import React from "react";
import { Text, TouchableOpacity, StyleSheet } from "react-native";
import PropTypes from "prop-types";
class RandomNumber extends React.Component {
static propTypes = {
id: PropTypes.number.isRequired,
number: PropTypes.number.isRequired,
isDisabled: PropTypes.bool.isRequired,
onPress: PropTypes.func.isRequired,
};
handlePress = () => {
if (this.props.isDisabled) {
return;
}
this.props.onPress(this.props.id);
};
render() {
return (
<TouchableOpacity onPress={this.handlePress}>
<Text style={[styles.random, this.props.isDisabled && styles.disabled]}>
{this.props.number}
</Text>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
random: {
backgroundColor: "#999",
width: 120,
marginHorizontal: 15,
marginVertical: 25,
fontSize: 35,
textAlign: "center",
},
disabled: {
opacity: 0.2,
},
});
export default RandomNumber;
Game.js:
import React from "react";
import { View, Text, Button, StyleSheet } from "react-native";
import PropTypes from "prop-types";
import RandomNumber from "./RandomNumber";
import shuffle from "lodash.shuffle";
class Game extends React.Component {
static propTypes = {
randomNumberCount: PropTypes.number.isRequired,
initialSeconds: PropTypes.number.isRequired,
onPlayAgain: PropTypes.func.isRequired,
};
state = { selectedIds: [], remainingSeconds: this.props.initialSeconds };
gameStatus = "PLAYING";
randomNumbers = Array.from({ length: this.props.randomNumberCount }).map(
() => 1 + Math.floor(10 * Math.random())
);
target = this.randomNumbers
.slice(0, this.props.randomNumberCount - 2)
.reduce((acc, curr) => acc + curr, 0);
shuffledRandomNumbers = shuffle(this.randomNumbers);
componentDidMount() {
this.intervalID = setInterval(() => {
this.setState(
(prevState) => {
return { remainingSeconds: prevState.remainingSeconds - 1 };
},
() => {
if (this.state.remainingSeconds === 0) {
clearInterval(this.intervalID);
}
}
);
}, 1000);
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
isNumberSelected = (numberIndex) => {
return this.state.selectedIds.indexOf(numberIndex) >= 0;
};
selectNumber = (numberIndex) => {
this.setState((prevState) => ({
selectedIds: [...prevState.selectedIds, numberIndex],
}));
};
UNSAFE_componentWillUpdate(nextProps, nextState) {
if (
nextState.selectedIds !== this.state.selectedIds ||
nextState.remainingSeconds === 0
) {
this.gameStatus = this.calcGameStatus(nextState);
if (this.gameStatus !== "PLAYING") {
clearInterval(this.intervalID);
}
}
}
calcGameStatus = (nextState) => {
const sumSelected = nextState.selectedIds.reduce((acc, curr) => {
return acc + this.shuffledRandomNumbers[curr];
}, 0);
if (this.state.remainingSeconds === 0) {
return "LOST";
}
if (sumSelected < this.target) {
return "PLAYING";
}
if (sumSelected === this.target) {
return "WON";
}
if (sumSelected > this.target) {
return "LOST";
}
};
render() {
const gameStatus = this.gameStatus;
return (
<View style={styles.container}>
<Text style={[styles.target, styles[`STATUS_${gameStatus}`]]}>
{this.target}
</Text>
<View style={styles.randomContainer}>
{this.shuffledRandomNumbers.map((randomNumber, index) => (
<RandomNumber
key={index}
id={index}
number={randomNumber}
onPress={this.state.selectNumber}
isDisabled={
this.isNumberSelected(index) || gameStatus !== "PLAYING"
}
/>
))}
</View>
{this.gameStatus !== "PLAYING" && (
<Button title="Play Again" onPress={this.props.onPlayAgain} />
)}
<Text>{this.state.remainingSeconds}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: "#ddd",
flex: 1,
},
target: {
fontSize: 40,
backgroundColor: "#bbb",
margin: 50,
textAlign: "center",
},
randomContainer: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-around",
},
STATUS_PLAYING: {
backgroundColor: "#bbb",
},
STATUS_WON: {
backgroundColor: "green",
},
STATUS_LOST: {
backgroundColor: "red",
},
});
export default Game;
App.js:
import React from "react";
import Game from "./Game";
class App extends React.Component {
state = { gameId: 1 };
resetGame = () => {
this.setState((prevState) => {
return { gameId: prevState.gameId + 1 };
});
};
render() {
return (
<Game
key={this.state.gameId}
onPlayAgain={this.resetGame}
randomNumberCount={6}
initialSeconds={10}
/>
);
}
}
export default App;
I've already tried some solutions from other posts, but none of them worked.
Any help is appreciated.
Related
I want to render the same component a fixed number of times(entered by the user) one after the other (Asynchronously). For this, I have used the below function passing as props approach where a while loop is used to make the code run asynchronously and could progress only after the state variable componentRendered becomes true, but it's not working. Could someone help to figure this out. Thanks in advance.
HomeScreen.js
import {View, Text} from 'react-native';
import Stepper from '../components/Stepper';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
componentRendered: false,
};
}
setComponentRender = () => {
this.setState({componentRendered: true});
};
render() {
var stepperElement = [];
for (let i = 1; i <= 2; i++) {
stepperElement.push(
<Stepper setComponentRender={this.setComponentRender} />,
);
while (this.state.componentRendered === false) {}
this.setState({componentRendered: false});
}
return <View>{stepperElement}</View>;
}
}
export default HomeScreen;
Stepper.js
import {View, TextInput, Text, Alert} from 'react-native';
import {ProgressSteps, ProgressStep} from 'react-native-progress-steps';
class Stepper extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
}
static navigationOptions = {
header: null,
};
defaultScrollViewProps = {
keyboardShouldPersistTaps: 'handled',
contentContainerStyle: {
flex: 1,
justifyContent: 'center',
},
};
onNextStep = () => {
console.log('called next step');
};
onPaymentStepComplete = () => {
Alert.alert(
'Payment completed!',
'Your Payment is Successful',
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{cancelable: false},
);
};
onPrevStep = () => {
console.log('called previous step');
};
onSubmitSteps = () => {
console.log('called on submit step.');
this.props.setComponentRender();
};
render() {
return (
<View style={{flex: 1, marginTop: 50}}>
<ProgressSteps>
<ProgressStep
label="Payment"
onNext={this.onPaymentStepComplete}
onPrevious={this.onPrevStep}
scrollViewProps={this.defaultScrollViewProps}>
<View style={{alignItems: 'center'}}>
<Text>Payment step content</Text>
</View>
</ProgressStep>
<ProgressStep
label="Confirm Order"
onPrevious={this.onPrevStep}
onSubmit={this.onSubmitSteps}
scrollViewProps={this.defaultScrollViewProps}>
<View style={{alignItems: 'center'}}>
<Text>Confirm order step content</Text>
</View>
</ProgressStep>
</ProgressSteps>
</View>
);
}
}
export default Stepper;
I am building a react native app that uses the Wordpress api. I am having problems displaying the cart and receive this error.
Object {} error [Error: Request failed with status code 404]
undefined
I have tried everything and have figures it might be a problem with Axios...
Please advise me on what I can do...
CartAction.js
import * as types from '../constants/ActionTypes';
import CartApi from '../api/CartApi';
export function getCart() {
return (dispatch) => {
return CartApi.getCart().then(cart => {
dispatch(getCartSuccess(cart));
}).catch(err => {
//TODO:get correct error msg
console.log(err.error);
})
};
}
function getCartSuccess(cart) {
return {
type: types.GET_CART_SUCCESS,
cart
};
}
export function addToCart(product, quantity) {
return (dispatch) => {
return CartApi.addToCart(product, quantity).then(cartUpdate => {
dispatch(addToCartSuccess(cartUpdate));
}).catch(err => {
//TODO:get correct error msg
console.log('error',err);
})
};
}
function addToCartSuccess(cartUpdate) {
return {
type: types.ADD_TO_CART_SUCCESS,
cartUpdate
};
}
CartPage.js
import React from 'react'
import { StyleSheet, Text, View, FlatList, Image } from 'react-native'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as CartAction from '../../actions/CartAction';
class CartPage extends React.Component {
constructor(props) {
super(props);
this.state = {
cart: {}
}
}
componentDidMount() {
this.props.CartAction.getCart();
}
_keyExtractor = (item, index) => item.key;
render() {
console.log(this.props.cart)
const cartObject = this.props.cart;
var cartArray = [];
Object.keys(cartObject).forEach(function(key) {
cartArray.push(cartObject[key]);
});
const Items = <FlatList contentContainerStyle={styles.list}
data={cartArray}
keyExtractor={this._keyExtractor}
renderItem={({ item }) =>
// <TouchableHighlight style={{width:'50%'}} onPress={() => navigate("Product", { product: item })} underlayColor="white">
<View style={styles.lineItem} >
<Image style={styles.image} source={{uri: item.product_image}} />
<Text style={styles.text}>{item.product_name}</Text>
<Text style={styles.text}>{item.quantity}</Text>
</View>
// </TouchableHighlight>
}
/>;
return (
<View style={styles.container}>
{Items}
</View>
)
}
}
const styles = StyleSheet.create({
lineItem: {
flexDirection: 'row'
},
list: {
flexDirection: 'column'
},
image: {
width: 50,
height: 50
},
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 20,
padding: 5
}
})
function mapStateToProps(state) {
return {
cart: state.cart
};
}
function mapDispatchToProps(dispatch) {
return {
CartAction: bindActionCreators(CartAction, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(CartPage);
<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 my React native project, I am using expo to build an application which will scan QR code. for that reason I have installed the following package using this command-
npm install expo-barcode-scanner
I have followed the below example given in snack expo-
https://snack.expo.io/BJlFFcp2g
When I run this example on my device it works.
But in my React-native project, when I copy the same code given in App.js file, after running the application the camera opens but it shows no result while scanning.
Here is the code-
App.js
import React, { Component } from 'react';
import { Alert, Linking, Dimensions, LayoutAnimation, Text, View, StatusBar, StyleSheet, TouchableOpacity } from 'react-native';
import { BarCodeScanner, Permissions } from 'expo';
export default class App extends Component {
state = {
hasCameraPermission: null,
lastScannedUrl: null,
};
componentDidMount() {
this._requestCameraPermission();
}
_requestCameraPermission = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({
hasCameraPermission: status === 'granted',
});
};
_handleBarCodeRead = result => {
if (result.data !== this.state.lastScannedUrl) {
LayoutAnimation.spring();
this.setState({ lastScannedUrl: result.data });
}
};
render() {
return (
<View style={styles.container}>
{this.state.hasCameraPermission === null
? <Text>Requesting for camera permission</Text>
: this.state.hasCameraPermission === false
? <Text style={{ color: '#fff' }}>
Camera permission is not granted
</Text>
: <BarCodeScanner
onBarCodeRead={this._handleBarCodeRead}
style={{
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
}}
/>}
{this._maybeRenderUrl()}
<StatusBar hidden />
</View>
);
}
_handlePressUrl = () => {
Alert.alert(
'Open this URL?',
this.state.lastScannedUrl,
[
{
text: 'Yes',
onPress: () => Linking.openURL(this.state.lastScannedUrl),
},
{ text: 'No', onPress: () => {} },
],
{ cancellable: false }
);
};
_handlePressCancel = () => {
this.setState({ lastScannedUrl: null });
};
_maybeRenderUrl = () => {
if (!this.state.lastScannedUrl) {
return;
}
return (
<View style={styles.bottomBar}>
<TouchableOpacity style={styles.url} onPress={this._handlePressUrl}>
<Text numberOfLines={1} style={styles.urlText}>
{this.state.lastScannedUrl}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.cancelButton}
onPress={this._handlePressCancel}>
<Text style={styles.cancelButtonText}>
Cancel
</Text>
</TouchableOpacity>
</View>
);
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000',
},
bottomBar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0,0,0,0.5)',
padding: 15,
flexDirection: 'row',
},
url: {
flex: 1,
},
urlText: {
color: '#fff',
fontSize: 20,
},
cancelButton: {
marginLeft: 10,
alignItems: 'center',
justifyContent: 'center',
},
cancelButtonText: {
color: 'rgba(255,255,255,0.8)',
fontSize: 18,
},
});
So, I am not getting - what should I do to make the QR code scanner working?
It would be very nice if someone suggests me to solve this or give me any example so that I can implement this.
I personally have had more success using the library at https://github.com/moaazsidat/react-native-qrcode-scanner for QR code scanning. Giving this a try may solve your problem? They have a getting started in the README that I recommend following in this case.
This is the code of my QR scanner
import React, { Component } from 'react';
import { Alert, View, Text, Vibration, StyleSheet } from 'react-native';
import { Camera, BarCodeScanner, Permissions } from 'expo';
export default class ExpoScanner extends Component {
constructor(props) {
super(props);
this.onBarCodeRead = this.onBarCodeRead.bind(this);
this.renderMessage = this.renderMessage.bind(this);
this.scannedCode = null;
this.state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
id_cedulacli: '',
placa:'',
monto:'',
fecha_pago:'Seleccione fecha'
};
}
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
await this.setState({hasCameraPermission: status === 'granted'});
await this.resetScanner();
}
multaEdit = ({data, type}) =>{
if ((type === this.state.scannedItem.type && data === this.state.scannedItem.data) || data === null) {
return;
}
this.setState({ scannedItem: { data, type } });
const {placa} = this.state;
const {monto} = this.state;
const {fecha_pago} = this.state;
Vibration.vibrate();
console.log(`EAN scanned: ${data}`);
fetch('http://10.10.20.88/parciall/editarMul.php', {
method: 'post',
header:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
placa: placa,
monto: monto,
id_cedulacli: ''+`${data}`,
fecha_pago: fecha_pago,
})
})
.then((response) => response.json())
.then((responseJson) =>{
if(responseJson == "ActualizaciĆ³n completada"){
alert(responseJson);
this.props.navigation.navigate("InicioScreen");
}else{
alert(responseJson);
}
})
.catch((error)=>{
console.error(error);
});
this.props.navigation.navigate('Inicio', { ean: data });
}
renderAlert(title, message) {
Alert.alert(
title,
message,
[
{ text: 'OK', onPress: () => this.resetScanner() },
],
{ cancelable: true }
);
}
onBarCodeRead({ type, data } ) {
if ((type === this.state.scannedItem.type && data === this.state.scannedItem.data) || data === null) {
return;
}
Vibration.vibrate();
this.setState({ scannedItem: { data, type } });
console.log(`EAN scanned: ${data}`);
}
renderMessage() {
if (this.state.scannedItem && this.state.scannedItem.type) {
const { type, data } = this.state.scannedItem;
return (
<Text style={styles.scanScreenMessage}>
{`Scanned \n ${type} \n ${data}`}
</Text>
);
}
return <Text style={styles.scanScreenMessage}>Focus the barcode to scan.</Text>;
}
resetScanner() {
this.scannedCode = null;
this.setState({
scannedItem: {
type: null,
data: null
}
});
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={styles.container}>
<View style={{ flex: 1 }}>
<BarCodeScanner
onBarCodeScanned={this.multaEdit}
style={StyleSheet.absoluteFill}
/>
{this.renderMessage()}
</View>
</View>
);
}
}
I am facing 2 issues related to length of my selected photos:
When selecting photos, it lets me to select 5 photos without any issue (it fits my length restriction), however it doesn't save the chosen photos, when I go to the next screen. In another scenario, when I am at the same screen where I choose photos and I choose 6 photos, it selects the 6 photo but the warning popup will appear and say that its currently supports 5, then when I go to next screen its saves the selected photos unlike previously.
If I deselect photos and then try to select another photos (still in my length limit) popup jumps with selection limit and doesn't let me choose photos, when I go to the next screen it saves the changes from previous selection and not from current one.
import React from 'react';
import {
View,
ScrollView,
Image,
Dimensions,
TextInput,
Text,
StatusBar,
TouchableHighlight,
Linking,
Keyboard,
CameraRoll,
KeyboardAvoidingView
} from 'react-native';
import {connect} from 'react-redux';
import {ActionCreators} from '../redux/actions';
import {bindActionCreators} from 'redux';
import Colors from '../constants/Colors';
import api from '../api';
import {
getEmailAddress,
showError,
renderMessageBar,
registerMessageBar,
unregisterMessageBar
} from '../utils/ComponentUtils';
import {
regularHeader,
mainHeader
} from '../utils/Headers';
import {NavigationActions} from 'react-navigation';
import {SelectionLimitDialog} from '../utils/Dialogs';
import {ifIphoneX, isIphoneX} from 'react-native-iphone-x-helper';
import {SafeAreaView} from 'react-navigation';
// specific component imports.
import {List, ListItem} from 'react-native-elements'
import {Button} from 'react-native-elements'
import Loader from '../components/Loader';
import LoaderError from '../components/LoaderError';
import SelectedPhoto from '../components/SelectedPhoto';
class MultiSafeeScreen extends React.Component
{
static navigationOptions = ({navigation}) => {
const {params} = navigation.state;
const isIncome = params ? params.isIncome : false;
const notificationAction = params ? params.notificationAction : () => {};
const isShowBack = params ? params.isShowBack : false;
return mainHeader({
isShowBack: isShowBack,
backAction: () => navigation.goBack(),
notificationAction: () => notificationAction(),
income: isIncome
});
};
constructor(props) {
super(props);
this.selectedPhotos = [];
this.state = {
photos: null,
loader: {
loading: 0,
message: "Loading photos..."
},
selectedPhotos: [],
supportLength: 5
};
this.props.navigation.setParams({notificationAction: this.onNotification, isIncome: this.props.isNewNotifications});
}
UNSAFE_componentWillReceiveProps(newProps) {
if (newProps.isNewNotifications !== this.props.isNewNotifications) {
this.props.navigation.setParams({notificationAction: this.onNotification, isIncome: newProps.isNewNotifications});
}
}
componentDidMount() {
registerMessageBar(this.refs.alert);
let options = {
first: 30,
assetType: 'Photos',
}
CameraRoll.getPhotos(options)
.then(r => {
this.setState({
photos: r.edges,
loader: {
loading: 1,
message: "Loading photos..."
},
});
})
.catch((err) => {
//Error Loading Images
});
StatusBar.setHidden(false);
}
componentWillUnmount() {
unregisterMessageBar();
this.props.setSelectedPhotos(0);
}
onNotification = () => {
this.props.setNewNotifications(false);
this.props.navigation.navigate("Notifications");
}
closeKeyboard = () => {
Keyboard.dismiss();
}
onSelectPhoto = (photo, index) => {
let photos = new Set([...this.selectedPhotos]);
let len = photos.size + 1 ;
console.log('photos')
if (len > this.state.supportLength) {
this.limitDialog.open();
this.setState({selectedPhotos: this.selectedPhotos});
this.props.setSelectedPhotos(len);
}
else {
photos.add(photo);
this.selectedPhotos = Array.from(photos);
}
}
onDeselectPhoto = (photo, index) => {
let photos = new Set([...this.state.selectedPhotos]);
let len = photos.size - 1;
photos.delete(photo);
this.setState({selectedPhotos: Array.from(photos)});
this.props.setSelectedPhotos(len);
}
onNext = () => {
this.props.navigation.navigate("MultiSafeeCreate", {
isShowBack: true,
selected: [...this.state.selectedPhotos]
});
}
renderLoader() {
let {width, height} = Dimensions.get('window');
let photoWidth = width/3;
if (this.state.loader.loading === 0) {
return <Loader style={{justifyContent: 'center', alignItems: 'center'}} message={this.state.loader.message}/>
}
else if (this.state.loader.loading === 2) {
return <LoaderError style={{justifyContent: 'center', alignItems: 'center'}} message={this.state.loader.message}/>
}
// if photos are null do nothing, else if empty show onbording
// if has photos show photos.
if (this.state.photos === null) {
return <Loader style={{justifyContent: 'center', alignItems: 'center'}} message={this.state.loader.message}/>
}
else {
return (
<View style={{width: width, maxHeight: 600}}>
<ScrollView >
<View style={{flexDirection: 'row', width: width, flexWrap: 'wrap',marginBottom:40, justifyContent: 'space-between'}}>
{
this.state.photos.map((p, i) => {
return (
<SelectedPhoto
key={i}
index={i}
style={{
width: photoWidth,
height: photoWidth,
}}
borderColor = "white"
limit={this.state.supportLength}
photo={p}
onSelectPhoto={this.onSelectPhoto}
onDeselectPhoto={this.onDeselectPhoto}
/>
);
})
}
</View>
</ScrollView>
<View style={{ position:'absolute', right:-15, top:475 }}>
<Button
onPress={this.onNext}
containerViewStyle={{width: width}}
backgroundColor={Colors.red}
title='NEXT' />
</View>
</View>
);
}
}
render() {
return (
<View style={{flex: 1, backgroundColor: Colors.white}}>
{this.renderLoader()}
<SelectionLimitDialog ref={(el) => this.limitDialog = el} />
{renderMessageBar()}
</View>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
function mapStatesToProps(state) {
return {
isNewNotifications: state.isNewNotifications
};
}
export default connect(mapStatesToProps, mapDispatchToProps)(MultiSafeeScreen);
I'm trying to return an array (GET_NUMBERS action) but the reducer seems to be returning a function. It's probably not even related to React/Redux but just javascript error. I just want 'numbers' to return an array of 10 elements. If 'shuffled' is an array, and I'm returning 'shuffled', why is the action returning a function? Please advise. Thank you very much in advance.
import { createStore } from 'redux';
const genArray = () => {
let numbers = new Array(10);
const shuffled = [];
numbers = numbers.fill(1).map((_, i) => i + 1);
while (numbers.length) {
const idx = numbers.length * Math.random() | 0;
shuffled.push(numbers[idx]);
numbers.splice(idx, 1);
}
return shuffled;
};
const numbers = genArray();
console.log(numbers);
console.log(typeof numbers);
const initialState = {
count: 0,
numbers
};
const countReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1
};
case 'DECREMENT':
return {
count: state.count - 1
};
case 'ZERO':
return {
count: 0
};
case 'POWER':
return {
count: Math.pow(state.count, 2)
};
case 'GET_NUMBERS':
return {
numbers: state.numbers
};
default:
return state;
}
};
export default createStore(countReducer);
EDIT: addition:
The reason I think it returns a function is the fact that in my Container, the only time I don't have a warning of mismatch is when I use a 'func' on numbers:
Counter.propTypes = {
count: PropTypes.number,
numbers: PropTypes.func,
increment: PropTypes.func,
decrement: PropTypes.func,
zero: PropTypes.func,
power: PropTypes.func
};
I've tried doing PropTypes.array but I get:
Warning: Failed prop type: Invalid prop numbers of type function
supplied to Counter, expected array.
index.ios.js
import React from 'react';
import {
AppRegistry
} from 'react-native';
import { Provider, connect } from 'react-redux';
import store from './src/store';
import Counter from './src/Counter';
import * as actions from './src/actions';
// map the state k
const mapStateToProps = state => ({
count: state.count,
numbers: state.numbers
});
const CounterContainer = connect(mapStateToProps, actions)(Counter);
const Countly = () => (
<Provider store={store}>
<CounterContainer />
</Provider>
);
AppRegistry.registerComponent('Countly', () => Countly);
actions.js
export const increment = () => {
const action = {
type: 'INCREMENT'
};
return action;
};
export const decrement = () => {
const action = {
type: 'DECREMENT'
};
return action;
};
export const zero = () => {
const action = {
type: 'ZERO'
};
return action;
};
export const power = () => {
const action = {
type: 'POWER'
};
return action;
};
export const numbers = () => {
const action = {
type: 'GET_NUMBERS'
};
return action;
};
Counter.js
import React, { PropTypes } from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
export default class Counter extends React.Component {
renderNumbers() {
console.log(this.props.numbers);
return (
<Text>{this.props.numbers}</Text>
);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.appName}>
Countly
</Text>
<Text style={styles.tally}>
Tally: {this.props.count}
</Text>
<Text style={styles.tally}>
Numbers: {this.renderNumbers()}
</Text>
<TouchableOpacity onPress={this.props.increment} style={styles.button}>
<Text style={styles.buttonText}>
+
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.props.decrement} style={styles.button}>
<Text style={styles.buttonText}>
-
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.props.power} style={styles.button}>
<Text style={styles.buttonText}>
pow
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.props.zero} style={styles.button}>
<Text style={styles.buttonText}>
0
</Text>
</TouchableOpacity>
</View>
);
}
}
Counter.propTypes = {
count: PropTypes.number,
numbers: PropTypes.func,
increment: PropTypes.func,
decrement: PropTypes.func,
zero: PropTypes.func,
power: PropTypes.func
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
appName: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
tally: {
textAlign: 'center',
color: '#333333',
marginBottom: 20,
fontSize: 25
},
button: {
backgroundColor: 'blue',
width: 100,
marginBottom: 20,
padding: 20
},
buttonText: {
color: 'white',
textAlign: 'center',
fontSize: 20
}
});