Cannot read property 'items' of undefined - react-native

I'm new on Jest + Enzyme and I been doing some test in my app, everything was good till I try to test onPress event in one of my components that renders a ScrollView that has a ListItem inside. The error is: Cannot read property 'items' of undefined
Below is my code:
List.js
import React from 'react';
import { Text, StyleSheet, Image, View, ScrollView, Alert} from 'react-native';
import { ListItem, Button, Card } from 'react-native-elements'
import {observer, inject} from 'mobx-react';
import {StackNavigator} from 'react-navigation';
#inject('ItemStore')
#observer
export default class List extends React.Component{
constructor(props){
super(props);
this.state = {
itemsSelected: [],
}
}
displayItem(item, index){
const {navigate} = this.props.navigation;
this.props.ItemStore.ItemStore.itemSelected(item);
navigate("OtherComp", {'index': index});
}
render(){
return(
<ScrollView>
{
this.props.ItemStore.items.length ?
this.props.ItemStore.items.map((item, i) => (
<ListItem roundAvatar avatar = {{uri: item.photo_uri}}
title = {item.name}
key = {i}
onPress = {this.displayItem.bind(this, item, i)}
onLongPress = {this.setitem.bind(this, i)}
containerStyle = {{ backgroundColor: this.state.itemsSelected.indexOf(i) >= 0 ? "#f1f1f1" : "#ffffff"}}
/>
)) : undefined
}
</ScrollView>
);
}
}
List.test.js
import 'react-native';
import React from 'react';
import Enzyme from 'enzyme';
import MyList from '../../components/list/List';
import {shallow} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import sinon from 'sinon';
import { ListItem } from 'react-native-elements'
const ItemStore = require('../../stores/ItemStore').default;
Enzyme.configure({adapter: new Adapter()});
describe('ItemList', () => {
it('click list item', () => {
const wrapper = shallow(<MyList ItemStore={ItemStore.ItemStore} />)
const render = wrapper.dive();
const displayItem = sinon.spy(MyList, "displayItem");
render.find(ListItem).forEach(child => {
child.simulate('onPress');
});
expect(displayItem.calledOnce).toBe(true);
});
});
I'm using multiple stores that's why the invoke it's store.store

Shouldn't this:
this.props.ItemStore.items.length
Be..
this.props.ItemStore.ItemStore.items.length ?

Related

React native todo application

I need your help. Now I'm trying to create a todo application.I will try to make an application on the logic of pure react.The problem is that when I start typing input, I get an error: undefined is not an object (evaluating 'e.target.value).Please help me with this task. I will be very grateful to you
Tasks.js
import React, {useState} from "react";
import {FlatList, StyleSheet, Text, View} from "react-native";
import Task_Item from "./Task_Item";
import Form from "./Form";
export default function Tasks() {
const [tasks, setTasks] = useState([]);
const [input, setInput] = useState('');
let addTask = (myInput) => {
if (myInput) {
let newTask = {
id: Date.now(),
name: myInput
}
setTasks([...tasks, newTask])
}
}
let handleInput = (e) => {
setTasks(e.target.value)
}
return (<View style={styles.all_list}>
<FlatList data={tasks}
renderItem={({item}) => (<Task_Item keyExtractor={item => item.id} el={item}/>)}
/>
<Form addTask={addTask} inputValue={handleInput}/>
</View>)
}
Task_item.js
import React from "react";
import {StyleSheet, TouchableHighlight, Text, ScrollView} from "react-native";
export default function Task_Item({el}) {
return (<ScrollView>
<TouchableHighlight>
<Text>{el.name}</Text>
</TouchableHighlight>
</ScrollView>)
}
Form.js
import React from "react";
import {KeyboardAvoidingView, StyleSheet, Text, TextInput} from "react-native";
export default function Form({inputValue, addTask}) {
return (<KeyboardAvoidingView>
<TextInput
placeholder='Type Text...'
onChangeText={inputValue}
/>
<Text
onPress={addTask}
>+</Text>
</KeyboardAvoidingView>)
}
I see two things you could improve.
The onChangeText method directly passes you the text, so you should either pass the setInput to your inputValue Form prop, either change the onChangeText to the onChange.
Using onChangeText
Tasks
<Form addTask={addTask} inputValue={setInput} />
Form.js
<TextInput placeholder="..." onChangeText={inputValue} />
And therefore you can delete the handleInput method.
with onChange
Tasks
<Form addTask={addTask} inputValue={handleInput} />
Form.js
<TextInput placeholder="..." onChange={handleInput} />
The second thing is that you handleInput method is changing your tasks and not your input...
const handleInput = (e) => setInput(e.target.value)
Your addTask is not getting the input... You could change your method to this:
const addTask = () => {
if (input) setTasks([...tasks, { id: Date.now(), name: input }])
}

React Navigation update screen on specific tab

I'm new on react native. I have navigation (bottom navigation) with Setting screen and TopNavigation.
Inside TopNavigation I have dynamic tab with 1 screen (multiple tab with 1 screen). The problem is, MainComponent.js would receive the results at componentWillReceiveProps() and how to send or update data from nextProps to my specific dynamic tab? Or maybe my code in the wrong way?
You can see my image, my multiple tab with 1 screen, and have 1 button to fetch data. And this is my code:
index.js
import React from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
//Redux
import {applyMiddleware, createStore} from 'redux';
import {Provider} from 'react-redux';
//reducers
import allReducers from './App/reducers';
import MainContainer from './App/Containers/MainContainer';
//Redux saga
import createSagaMiddleware from 'redux-saga';
import rootSaga from './App/sagas/rootSaga';
const sagaMiddleware = createSagaMiddleware();
let store = createStore(allReducers, applyMiddleware(sagaMiddleware));
const Main = () => (
<Provider store={store}>
<MainContainer />
</Provider>
);
sagaMiddleware.run(rootSaga);
AppRegistry.registerComponent(appName, () => Main);
MainContainer.js
import {connect} from 'react-redux';
import MainComponent from '../Components/MainComponent';
import {fetchCategoriesAction} from '../actions/categoriesAction';
import {fetchTestAction} from "../actions/testAction";
const mapStateToProps = (state) => {
return {
receivedCategories: state.categoriesReducer,
receivedMovies: state.testingReducer,
navigation: state.navigation
}
};
const mapDispatchToProps = (dispatch) => {
return {
onFetchCategories: (payload) => {
console.log("mapDispatchToProps");
dispatch(fetchCategoriesAction(payload));
},
onFetchTest: (payload) => {
dispatch(fetchTestAction(payload))
}
};
};
const MainContainer = connect(mapStateToProps, mapDispatchToProps)(MainComponent);
export default MainContainer;
MainComponent.js
import React, {Component} from "react";
import {createAppContainer, createMaterialTopTabNavigator} from 'react-navigation'
import {createMaterialBottomTabNavigator} from "react-navigation-material-bottom-tabs";
import {tabBarOptions} from "../Navigation/Top/Options";
import Test from "./Screens/Test";
import Setting from "./Screens/Setting";
export default class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {myNavigator: null, movies: [], categories: []}
}
componentWillMount() {
this.props.onFetchCategories({p1: 1});
}
createNavigator(categories) {
if (categories != null) {
const screens = {};
categories.forEach(page => {
screens[page.slug] = {
screen: Test,
};
});
let TopNavigator = createMaterialTopTabNavigator(screens, {
tabBarOptions,
lazy: true,
});
const AppNavigator = createMaterialBottomTabNavigator({
B1: TopNavigator,
B2: Setting,
});
const AppContainer = createAppContainer(AppNavigator);
this.setState({myNavigator: <AppContainer screenProps={this.props}/>});
}
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
if (nextProps.receivedCategories !== null && nextProps.receivedCategories.categories !== this.state.categories) {
this.setState({categories: nextProps.receivedCategories.categories});
this.createNavigator(nextProps.receivedCategories.categories)
}
}
render() {
return this.state.myNavigator;
}
}
Test.js
import React, {Component} from "react";
import {Text, Container, Button} from 'native-base'
export default class Test extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Container>
<Button onPress={ () => {
this.props.screenProps.onFetchTest({slug: this.props.navigation.state.routeName})
}}><Text>Fetch Data</Text></Button>
<Text>Test screen {this.props.navigation.state.routeName}</Text>
<Text>I want this part changed when i click fetch data button</Text>
</Container>
)
}
}
Thank you!

how can use a function in props of react native?

I need to pass a function on the props. I have this component:
import React, {Component} from 'react';
import { View } from 'react-native';
import {FBLogin, FBLoginManager} from 'react-native-facebook-login';
const Loginfb = (props) => (
<FBLogin
style={{marginBottom: 10}}
ref={props.ref}
permissions={["email", "user_friends"]}
loginBehavior={FBLoginManager.LoginBehaviors.SystemAccount}
onLogin={props.login}
);
export default Loginfb;
Where props.ref and props.login are functions with data. In my container component I have this:
import React, {Component} from 'react';
import {View} from 'react-native';
import {FBLogin, FBLoginManager} from 'react-native-facebook-login';
import Loginfb from '../components/fblogin';
class Inicio extends Component {
constructor(props) {
super(props);
this.state = {
user: null,
};
}
Ref = (fbLogin) => {
this.fbLogin = fbLogin
}
login = (data) => {
console.log("Logged in!");
console.log(data);
this.setState({user: data.credentials});
}
render() {
return (
<View
<Loginfb
ref={this.ref}
onLogin={this.login}/>
</View>
);
}
}
export default Inicio;
I can't understand my error: "this.props[event] is not a function"
Please Help.
Not quite sure, but I'm assuming the context is lost.
Either inline when you pass them to the child:
myFunction.bind(this)
Or in the constructor of the container, like so:
this.myFunction = this.myFunction.bind(this)
Hope this helps.

Navigation in React Router v4 Native Not Changing Scenes

I'm using React Router for a React Native app. Not sure what I'm missing here but I install react-router-native and require the history package, and setup a couple methods that just push a new route onto the stack but nothing happens. I console.log('clicked'); to check that it's firing and it is so not sure what's wrong.
import React, { Component } from 'react';
import { View } from 'react-native';
import Splash from './Splash';
import createHistory from 'history/createMemoryHistory';
const history = createHistory();
class SplashContainer extends Component {
goToLogin = () => {
history.push('/Login');
}
goToRegister = () => {
history.push('/SignUp');
}
render () {
console.log(history)
return (
<Splash
goToLogin={this.goToLogin}
goToRegister={this.goToRegister}
/>
);
}
}
export default SplashContainer;
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { Button } from 'native-base';
import { Link } from 'react-router-native';
import PropTypes from 'prop-types';
const Splash = (props) => {
console.log(props)
return (
<View style={styles.container}>
<Button light block onPress={props.goToLogin}>
<Text>Login</Text>
</Button>
<Button dark block bordered style={{marginTop: 10}} onPress={props.goToRegister}>
<Text>Register</Text>
</Button>
</View>
);
}
Splash.propTypes = {
goToLogin: PropTypes.func.isRequired,
goToRegister: PropTypes.func.isRequired
}
export default Splash;
I don't know your Router config, but your methods should be:
goToLogin = () => {
const { history } = this.props
history.push('/Login');
}
history will passed down via props of component inside Router's stack.

react-native / redux - action not working?

I'm playing with react-native / redux and am dispatching an action that is supposed to display a number yet an error gets thrown:
Unhandled JS Exception: Objects are not valid as a React child (found:
object with keys {type, payload}). If you meant to render a collection
of children, use an array instead or wrap the object using
createFragment(object) from the React add-ons. Check the render method
of Text.
createStore.js
import { createStore, applyMiddleware, combineReducers } from 'redux';
import createLogger from 'redux-logger';
import numReducer from './reducers/numReducer';
const logger = createLogger();
export default (initialState = {}) => (
createStore(
combineReducers({
numbers: numReducer
}),
initialState,
applyMiddleware(logger)
)
);
App.js
import React from 'react';
import { Provider } from 'react-redux';
import HomeScreen from './components/HomeScreen';
import createStore from './createStore';
const store = createStore();
export default () => (
<Provider store={store}>
<HomeScreen />
</Provider>
);
numReducer.js
import { LIST_NUMBERS, PICK_NUMBER } from '../actions/actionTypes';
export default (state = [], action = {}) => {
switch (action.type) {
case LIST_NUMBERS:
return action.payload || [];
case PICK_NUMBER:
return action.payload;
default:
return state;
}
};
HomeScreen.js
import React from 'react';
import { View } from 'react-native';
import NavContainer from '../containers/NavContainer';
const HomeScreen = () => (
<View>
<NavContainer />
</View>
);
export default HomeScreen;
NavContainer.js
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { listNumbers, pickNumber } from '../actions/numberActions';
import Nav from '../components/Nav';
const mapStateToProps = state => ({
numbers: state.numbers
});
const mapDispatchToProps = dispatch => (
bindActionCreators({
listNumbers,
pickNumber
}, dispatch)
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Nav);
Nav.js
import React, { Component, PropTypes } from 'react';
import { View, Text } from 'react-native';
export default class Nav extends Component {
render() {
return (
<View>
<Text>FirstLine</Text>
<Text>SecondLind</Text>
<Text>Number: {this.props.pickNumber(3)}</Text>
</View>
);
}
}
Please advise what I am doing wrong. Thank you
You need to dispatch your action from inside one of your lifecycle methods or on some handler, and then use the (updated) props from your redux store in your component.
Example:
import React, { Component, PropTypes } from 'react';
import { View, Text } from 'react-native';
export default class Nav extends Component {
componentDidMount() {
this.props.pickNumber(3);
}
render() {
return (
<View>
<Text>FirstLine</Text>
<Text>SecondLind</Text>
<Text>Number: {this.props.numbers}</Text>
</View>
);
}
}