Cannot read property onSubmit of undefined - reactjs

Trying to pass down a function as a prop:
import {
...
} from "react-native";
import {
...
} from "../actions/events";
import { isLoading, isLoadingCredentials, hasErrored } from "../actions/loader";
class HomeScreen extends React.PureComponent {
static navigationOptions = {
title: "",
headerMode: "screen",
header: null
};
constructor(props) {
super(props);
}
componentWillMount() {
const {
navigation: { navigate },
setCredentials
} = this.props;
}
onSubmit(e) {
...
}
render() {
const {
...
} = this.props;
if (!authenticated) {
return <Login onPress={this.onAuthenticate} />;
}
return (
<View
style={styles.container}
onLayout={this.onLayout.bind(this)}
>
<Pickers
onSubmit={this.onSubmit}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
categories: state.fetchCategories,
isLoading: state.isLoading,
hasErrored: state.hasErrored,
credentials: state.setCredentials,
isLoadingCredentials: state.isLoadingCredentials,
authenticated: state.authenticated
};
};
const mapDispatchToProps = {
....
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen);
Child:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Picker from "./common/Picker";
import {
..
} from "react-native";
import {
...
} from "../actions/events";
export class Pickers extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
const {
credentials: { year, group, student, showStudent }
} = this.props;
return (
<View>
<TouchableHighlight
onClick={() => this.props.onSubmit()}
>
<Text style={styles.buttonText}> Submit</Text>
</TouchableHighlight>
</View>
);
}
}
const mapStateToProps = state => {
return {
credentials: state.setCredentials
};
};
const mapDispatchToProps = {
...
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Pickers);
What am I doing wrong?

Change it to:
render() {
const { onSubmit } = this.props;
return <TouchableHighlight
onClick={this.props.onSubmit}
/>
}
Your onSubmit method is being passed from the Props not on the class.
So it is undefined.
Hence You need to call this.props.onSubmit while submitting.
It will call the function which is passed as the prop from your Parent Component.
And yeah now you should switch to the ES6 syntax.
class HomeScreen extends React.PureComponent {
static navigationOptions = {
title: "",
headerMode: "screen",
header: null
};
constructor(props) {
super(props);
}
onSubmit= (e) => {
...
}
render(){
<Pickers onSubmit={this.onSubmit}/>}

Related

Accessing a function inside HOC

I just want to access componentDidMount in test file, how can I write a test case so that I can access componentDidMount? I just want to print hi in the console.
import React, { Component } from 'react';
export const MyComponent = (functionName, secondFunctionName) => (
WrappedComponent,
) => {
class WithWrapped extends Component {
constructor(props) {
super(props);
this.getEmployees = props.getEmployees;
this.clearEmployees = props.clearEmployees;
this.state = { user: 'test', loading: true };
this.refreshEmp = this.refreshEmp.bind(this);
}
componentDidMount() {
console.log('Hi')
}
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
}
render() {
}
}
const mapStateToProps = (state) => {
return {
};
};
const mapDispatchToProps = (dispatch) => ({
});
return connect(mapStateToProps, mapDispatchToProps)(WithWrapped);
};

Props not displaying from fetch call

I am trying to display recipes and not sure if I have this setup correctly. I am pulling recipes from a rails api via get fetch request. At the moment nothing is displaying.
Here is my recipe container:
import React, { Component } from 'react'
import RecipeList from '../components/RecipeList'
import RecipeInput from '../components/RecipeInput'
import { connect } from 'react-redux'
import { postRecipes } from '../actions/postRecipes.js'
import { getRecipes } from '../actions/getRecipes'
class RecipeContainer extends Component{
constructor(props){
super(props)
}
componentDidMount(){
getRecipes()
}
render(){
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} />
</div>
)
}
}
const mapStateToProps = state =>({
recipes: state.recipes
})
const mapDispatchToProps = dispatch =>{
return{
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
// deleteRecipe: id => dispatch({type: 'Delete_Recipe', id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeContainer)
Here is my get request....notice that I am returning my Recipe component here.
export const getRecipes = () => {
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
dispatch({ type: 'START_FETCHING_RECIPES_REQUEST' });
fetch(RECIPES_URL)
.then(response =>{ return response.json()})
.then(recipes => dispatch({ type: 'Get_Recipes', recipes }));
};
}
This is where I am trying to render the Recipe component from the get request
import React, {Component} from 'react';
// import { getRecipes } from '../actions/getRecipes.js';
import Recipe from './Recipe.js'
class RecipeList extends Component {
// componentDidMount(){
// getRecipes()
// }
render() {
return (
<div>
{this.props.recipes.map(recipe => (<Recipe recipe={recipe} key={recipe.id} />))}
</div>
)
}
}
export default RecipeList;
Edit: Added reducer
switch(action.type){
case 'Add_Recipe':
const recipe = {
name: action.name,
ingredients: action.ingredients,
chef_name: action.chef_name,
origin: action.origin,
category: action.category
}
return{
...state,
recipes: [...state.recipes, recipe],
}
case 'START_FETCHING_RECIPES_REQUEST':
return {
...state,
recipes: [...state.recipes],
requesting: true
}
case 'Get_Recipes':
return {
...state, recipes: action.recipes,
requesting: false
}
default:
return state
}
}
How can I correct this to make it work?
Issue
You are not passing the recipes to the RecipeList component that were fetched and presumably stored in state, and fed back to the UI via RecipeContainer.
Solution
Pass the recipe state from RecipeContainer to RecipeList as a prop. and then render/map the recipes from props.
RecipeContainer
class RecipeContainer extends Component{
componentDidMount() {
getRecipes();
}
render() {
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} /> // <-- pass recipe state
</div>
)
}
}
const mapStateToProps = state => ({
recipes: state.recipes,
});
const mapDispatchToProps = dispatch => {
return {
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
}
};
RecipeList
class RecipeList extends Component {
render() {
const { recipes } = this.props;
return (
<div>
{recipes.map(recipe => (
<Recipe recipe={recipe} key={recipe.id} />
))}
</div>
);
}
}
The actual solution to this was I needed to have an explicit return in my mapStateToProp function.
Eg.
const mapStateToProp = state =>{
return {
recipes: state.recipes
}
}

asynchron firestore query with mapStateToProps

I would like to use some data I received from firestore to build a quiz. Unfortunately I can console.log the array, but if I use .length it is undefined.
Is this problem caused by some lifecycle or asnynchronous issue?
Thanks in advance!
import React, { Component } from 'react';
import { connect } from 'react-redux';
class LernenContainer extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
render() {
return (
<div className="lernenContainer">
LernenContainer
{
console.log(this.props.firestoreData),
// prints array correctly
console.log(this.props.firestoreData.length)
// is undefined
}
</div>
);
}
}
const mapStateToProps = state => {
return {
firestoreData: state.firestoreData
};
};
const mapDispatchToProps = dispatch => {
return {
// todo Achievements
};
};
export default connect(mapStateToProps, mapDispatchToProps) (LernenContainer);
console.log(this.props.firestoreData):
Try below code
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types'
class LernenContainer extends Component {
constructor(props) {
super(props);
}
static propTypes = {
firestoreData: PropTypes.object.isRequired
}
render() {
const { firestoreData } = this.props
console.log(firestoreData);
console.log(firestoreData.length);
return (
<div className="lernenContainer">
</div>
);
}
}
const mapStateToProps = (state) => ({
firestoreData: state.firestoreData
})
const mapDispatchToProps = (dispatch) => ({
})
export default connect(mapStateToProps,mapDispatchToProps)(LernenContainer);

Rerender component child after after state change in parent component

Hello am trying to refresh the graph after changing the value of select option but it shows the first graph and when I change the select option the state is changed but the graph didn't change I think the problem is in lifecycle component when the state changes didn't change only rendred for one time how can I fix it and thank you
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Select from "react-select";
import Graph from "../graph/Graph";
class Home extends Component {
state = {
selectedOption: null
};
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
render() {
const { user } = this.props.auth;
const { organization } = user;
console.log(organization);
//const organization = user.organization;
console.log(user);
//let organization = user.organization[0];
const options = organization.map(org => ({
value: org.conceptPrefix,
label: org.name
}));
const { selectedOption } = this.state;
let graphObject;
if (selectedOption == null) {
graphObject = <h4>Choose Organization</h4>;
} else {
graphObject = (
<div>
<Graph org={this.state.selectedOption.value} />
</div>
);
}
return (
<div>
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
/>
{graphObject}
</div>
);
}
}
Home.propTypes = {
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
graph: state.graph
});
export default connect(
mapStateToProps,
{}
)(Home);
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { graphGet } from "../../actions/graphActions";
import GraphImp from "./GraphImp";
class Graph extends Component {
constructor(props) {
super(props);
this.state = {
org: props.org
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
componentDidMount() {
this.props.graphGet(this.props.org);
}
render() {
// {this.props.graph.graph && this.state.formSubmitted
// ? this.createList()
// : "wait Graph"}
const { graph, loading } = this.props.graph;
let graphContent;
if (graph == null || loading) {
graphContent = <h4>Loading ...</h4>;
} else {
graphContent = <GraphImp grapheData={graph} />;
}
return <div>{graphContent}</div>;
}
}
Graph.prototypes = {
graphGet: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
graph: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
graph: state.graph,
errors: state.errors
});
export default connect(
mapStateToProps,
{ graphGet }
)(Graph);
There are 2 ways to achieve your goal.
First option: Implement componentDidUpdate in Graph
componentDidUpdate(prevProps) {
if(prevProps.org !== this.props.org) {
this.setState({ org: this.props.org });
this.props.graphGet(this.props.org);
}
}
Second option: Force react to fully remount&render your graph whenever you change the option by changing the key (Make sure the key is not an object/array)
<Graph key={this.state.selectedOption.value} org={this.state.selectedOption.value} />

React Native with Redux : data is loaded, render shows empty

I have a react native app with react-redux , redux-persist and redux-thunk.
in the Component, I'm rendering the data from props, if the data length is less than one, i show an error, no data available.
it's always showing 'no data available' but actually data is in the props. as i check the console logs, ( using redux-logger ) data is available in the props.
if i put forceUpdate() at componentDidMount doesnt even help.
but if i put the forceUpdate() with a timeout it will load the data.
setTimeout(()=>{
this.forceUpdate();
}, 1000);
What could be the problem? Is render happening before data loads from props?
CoursesPage.js
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import Courses from "./components/Courses";
import {Actions as routes} from "react-native-router-flux";
import * as courseActions from "./courses.actions";
function mapStateToProps(state) {
return {
user: state.auth.user,
users: state.auth.users,
courses: state.courses.courses,
lectures: state.courses.lectures,
courseDetails: routes.courseDetails,
openProfile: routes.profilePage
}
}
function dispatchToProps(dispatch) {
return bindActionCreators({
getCourses: courseActions.getCourses
}, dispatch);
}
export default connect(mapStateToProps, dispatchToProps)(Courses);
Courses.js
import React, {Component, PropTypes} from "react";
import {
ActivityIndicator,
ListView,
StyleSheet,
Text,
View,
Image,
NetInfo,
Alert,
TouchableOpacity,
ScrollView,
Dimensions,
Platform,
RefreshControl
} from 'react-native';
import { Loader, Accordion, I18n, CustomNavBar, CustomAccordion } from "../../common/components";
import styles from "../../common/styles";
let DeviceInfo = require('react-native-device-info');
import Icon from 'react-native-vector-icons/Ionicons';
let { width, height } = Dimensions.get('window');
export default class Courses extends Component {
static propTypes = {
user: PropTypes.string.isRequired,
users: PropTypes.object.isRequired,
courseDetails: PropTypes.func.isRequired,
courses: PropTypes.object.isRequired,
getCourses: PropTypes.func.isRequired,
openProfile: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
isLoading: false,
isRefreshing: false
};
this._isMounted = false;
}
componentWillMount(){
this._isMounted = true;
const { users, getCourses } = this.props;
getCourses(users);
}
componentWillUnmount(){
this._isMounted = false;
}
componentWillReceiveProps(){
setTimeout(()=>{
this.forceUpdate();
}, 1000);
}
componentDidMount(){
setTimeout(()=>{
this.forceUpdate();
}, 1000);
setTimeout(()=>{
this.forceUpdate();
}, 2000);
}
async loadData(){
await this.props.getCourses(this.props.users);
setTimeout(()=>{
this.forceUpdate();
}, 1000);
}
selectRow(courseData) {
this.props.courseDetails({
courseData: courseData
});
}
renderData(containerList){
/* rendering .... */
}
render() {
const {user, users, getCourses, courses, openProfile} = this.props;
const data = courses[user];
let containerList = [];
Object.keys(data).forEach((d)=>{
let courseList = [];
Object.keys(data[d].courses).forEach((c)=>{
courseList.push(data[d].courses[c]);
});
containerList.push({
id: data[d].id,
title: data[d].title,
courses: courseList
});
});
return (
<View style={styles.container}>
<View style={{ width: width, height: Platform.OS == "ios" ? 64 : 54}}>
<CustomNavBar
width={width}
height={Platform.OS == "ios" ? 64 : 54}
title={I18n.t("details_page_book_button")}
titleSize={18}
buttonSize={15}
background={"#00a2dd"}
color={"#FFF"}
rightIcon={"ios-person-outline"}
rightIconSize={30}
rightAction={()=> { openProfile(); }}
/>
</View>
<View style={{ height: Platform.OS == "ios" ? height - 114 : height - 130 }}>
{!this.state.isLoading ?
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this.loadData.bind(this)}
tintColor="#00a2dd"
title=""
titleColor="#00a2dd"
colors={['#00a2dd', '#00a2dd', '#00a2dd']}
progressBackgroundColor="#FFFFFF"
/>
}
>
{this.renderData(containerList)}
</ScrollView>
:<ActivityIndicator
animating={true}
style={{ paddingTop: Platform.OS == "ios" ? (height - 114)/2 : (height - 130)/2 }}
color={'#00a2dd'}
size={'small'}
/>}
</View>
</View>
);
}
}
I think you dont change state so it is seen same data.So I suggest you should change code like following.Also you should immutable js to change state.
courseActions:
export function getCoursesRequest () {
return {
type: "GET_COURSES_REQUEST"
}
}
export function getCoursesSuccess (json) {
return {
type: "GET_COURSES_SUCCESS",
payload: json
}
}
export function getCoursesFailure (json) {
return {
type: "GET_COURSES_FAILURE",
payload: json
}
}
export function getCourses (sessionToken) {
return dispatch => {
dispatch(getCoursesRequest())
// store or get a sessionToken
return appAuthToken.getSessionToken(sessionToken)
.then((token) => {
return BackendFactory(token).getCourses()
})
.then((json) => {
dispatch(getCoursesSuccess(json))
})
.catch((error) => {
dispatch(getCoursesFailure(error))
})
}
}
coursesInitialState
const {Record} = require("immutable");
var InitialState = Record({
courses: {}
});
export default InitialState;
reducer:
const InitialState = require("./authInitialState").default;
const initialState = new InitialState();
export const courseReducer = (state = initialState, action) => {
if (!(state instanceof InitialState)) return initialState.mergeDeep(state);
switch (action.type) {
case "GET_COURSES_SUCCESS":
const {value} = action.payload;
let nextState = state.setIn(["courses"], value;
return nextState;
case "GET_COURSES_FAILURE":
}
}

Resources