react native pass function to child component as a prop (this.props.functionName is not a function) - function

I've seen this question a lot of other places too, but for some reason no matter what i do, binding or declaring differently, i keep receiving the same error that _this3.props.toggleProp() is not a function. (In '_this3.props.toggleProp()', '_this3.props.toggleProp is undefined.)
my parent component is:
constructor (props) {
super(props)
this.state = {
selectedTab: 'home',
notificationNumber: -1,
}
this._toggleScreen = this._toggleScreen.bind(this);
this.toggleSchedule = this.toggleSchedule.bind(this);
}
_toggleScreen() {
this.setState({
selectedTab: 'categories',
})
}
render(): React$Element<any> {
function MainContent(props) {
const selectedTab = props.selectedTab;
if (selectedTab === 'home') {
return <Home toggleProp={this._toggleScreen} grain="this one here"/>;
}
if (selectedTab === 'categories') {
return <Categories toggleScreen={this.toggleScreen} />;
}
return <Schedule />;
}
return (
<View style={styles.container}>
<MainContent selectedTab={this.state.selectedTab} style={styles.content}/>
</View>
);
}
}
and the important part of my child component is:
render(): React$Element<any> {
return (
<Icon.Button name="home" backgroundColor="rgba(0,0,0,0)" onPress={()=>{this.props.toggleProp()}}>
</Icon.Button>
i have constructor (props) {
super(props)
at the top. any ideas what's going on?

onPress is not a react SyntheticEvent
https://facebook.github.io/react/docs/events.html
change onPress for onClick and it should work.
here is a codesand with onClick working just fine.
https://codesandbox.io/s/93ZyOWl8

The function in your parent component is _toggleScreen() {}, however you're passing in this.toggleScreen instead of this._toggleScreen into your Categories component. This is part of what is causing the toggleProp() is not a function error.
if (selectedTab === 'categories') {
return <Categories toggleScreen={this._toggleScreen} />;
}
Additionally, you're using toggleProp as the prop in the <Home /> Component, but are using toggleScreen as the prop in your Categories component, so this would also throw a toggleProp is undefined error.
A working render function should look like this:
render(): React$Element<any> {
function MainContent(props) {
const selectedTab = props.selectedTab;
if (selectedTab === 'home') {
return <Home toggleProp={this._toggleScreen} grain="this one here"/>;
}
if (selectedTab === 'categories') {
return <Categories toggleProp={this._toggleScreen} />;
}
return <Schedule />;
}
return (
<View style={styles.container}>
<MainContent selectedTab={this.state.selectedTab} style={styles.content}/>
</View>
);
}

I actually needed to pass the function down two children, I totally forgot that I'm rendering the content in MainContent, so I need to pass the toggleScreen as a prop in the mainContent, then pass this.prop._toggleScreen to the home component then call it in there as a prop again.
handler(e) {
this.setState({
selectedTab: 'categories',
})
}
render(): React$Element<any> {
function MainContent(props) {
const selectedTab = props.selectedTab;
if (selectedTab === 'home') {
return <Home handler={props.handler} grain={props.selectedTab} />;
}
else if (selectedTab === 'categories') {
return <Categories toggleScreen={this.toggleScreen} />;
}
return <Schedule />;
}
return (
<View style={styles.container}>
<MainContent selectedTab={this.state.selectedTab} style={styles.content} handler={this.handler}/>
</View>
);
}
}

Related

React-Grid-Layout error: Uncaught Error: <DraggableCore> not mounted on DragStart

I have an error "Uncaught Error: not mounted on DragStart!" by using this library.
Well, I have a wrapper for the library's component:
const GridLayout = (props) => {
return <ReactGridLayout {...props} />
}
export default GridLayout
Then I have React class component:
export default class Dashboard extends React.Component<DashboardProps, any> {
constructor(props: DashboardProps) {
super(props)
this._dashboardScrollContainer = React.createRef()
this._dashboardState.onShow()
}
componentDidMount() {
this.syncScroll()
}
private readonly _dashboardScrollContainer: React.RefObject<HTMLDivElement>
render() {
const className = classNames(styles.Container, 'DashboardScrollContent')
return (
<Layout>
{this.renderToolbar()}
<div
ref={this._dashboardScrollContainer}
onScroll={this.onScroll}
className={className}
>
{this.renderContent()}
</div>
</Layout>
)
}
renderToolbar() {
if (!ui().user.isModeller && this._mode.isFreeMode) {
return null
}
return <DashboardToolbar />
}
renderContent() {
if (this._api.isLoading) {
return <LocalLoader />
}
if (!ui().user.isModeller && this._mode.isFreeMode) {
return null
}
const { width } = this._layout.gridLayoutSetting
return (
<div
className={styles.Content}
style={{ width }}
>
{this.renderGridLayout()}
</div>
)
}
renderGridLayout() {
if (_.isEmpty(this._layout.layoutMap)) {
return null
}
return (
<GridLayout
layout={this._layout.layoutMap}
onLayoutChange={this.onLayoutChange}
{...this._layout.gridLayoutSetting}
>
{this.renderCards()}
</GridLayout>
)
}
renderCards() {
const options = _.filter(this._api.options, (item) => {
return !!item.type && !item.toolbarId
})
return _.map(options, ({ id, type }) => {
return (
<RootCardComponent
key={id}
cardId={id}
cardType={type}
/>
)
})
}
}
If I try to move one of the cards, I get this error.
I had an old version of this library and everything worked well.
But now I have the version 1.3.4 and this error.
I also tried using new components from library, where we should import not the "ReactGridLayout", but "GridLayout" from 'react-grid-layout', but it doesn't help.
Wrapping component into React.forwardRef and then using ref attribute also doesn't help. (this way is recommended here react-grid-layout Error: <DraggableCore> not mounted on DragStart)
Maybe could someone helps with this issue?
thanks for all in advance!

React-native: how to get JSON from backend and put the objects in react-native elements, such as Text?

I am trying to fetch JSON data from a php file, which seems to work fine; I can alert values from the JSON. But I want to be able to put these values on the mobile app screen in Text elements or whatever. And I want this to happen when the screen opens, not when a button is pressed. So I made a function that fetches the JSON and I'm trying to return a value in Text elements. This function is called from the rendering. I don't get error messages, but it isn't working. Nothing shows up.
Here is the code:
import React, { Component } from 'react';
import { View, Text, AsyncStorage, Alert } from 'react-native';
import { UsersMap } from '../UsersMap';
import { PrimaryButton } from '../Buttons';
import styles from './styles';
class RestOptions extends Component {
getSearchResults() {
fetch('http://192.168.1.3/Restaurant_App/php/search_results.php')
.then((response) => response.json())
.then((responseJson) => {
var JSON_Test = responseJson["distance"][0];
//Alert.alert(JSON_Test);
return (
<View>
<Text>{JSON_Test}</Text>
</View>
);
}).catch((error) => {
console.error(error);
});
}
setReservation = () => {
this.props.navigation.navigate('SetReservation');
};
render() {
return (
<View>
<UsersMap />
{this.getSearchResults()}
<PrimaryButton
label="Set Reservation"
onPress={() => this.setReservation()}
/>
</View>
);
}
};
export default RestOptions;
This is what happens. The JSON value should appear between the map and the button:
Search Results Screen
First of all, in order to fetch the data as the screen opens, you should use the lifecycle method componentWillMount, which executes before the first render, and then store the result in the component's state. React docs on state and lifecycle
class RestOptions extends Component {
constructor(props) {
super(props);
this.state = {
jsonTest: null
}
}
componentWillMount() {
this.getSearchResults();
}
getSearchResults() {
fetch('http://192.168.1.3/Restaurant_App/php/search_results.php')
.then((response) => response.json())
.then((responseJson) => {
var JSON_Test = responseJson["distance"][0];
//Alert.alert(JSON_Test);
this.setState({ jsonTest: JSON_Test });
}).catch((error) => {
console.error(error);
});
}
//...
Then you can display the value on the render() method:
render() {
return (
<View>
<UsersMap />
<Text>{this.state.jsonTest}</Text>
<PrimaryButton
label="Set Reservation"
onPress={() => this.setReservation()}
/>
</View>
);
}
If the response is an array of values, you can use map() to display each of them in their own Text element:
{this.state.jsonTest.map((value, index) => <Text key={index}>{value}</Text>)}

React how to get wrapped component's height in HOC?

Is there any way to get the wrapped component's DOM height?
I tried adding an ref but the console errors me Function components cannot be given refs.
And I set the forward ref, but it seems not the case.
export default function withInfiniteScroll(Component) {
return class extends React.Component {
componentDidMount() {
window.addEventListener('scroll', this.onScroll, true);
}
onScroll = () => {
// here
console.log(
'window.innerHeight👉', window.innerHeight,
'\ndocument.body.offsetHeight👉', document.body.offsetHeight,
);
}
render() {
return <Component {...this.props} />;
}
};
}
I want to log the height of Component, but these logs are meaningless, they are html-body's height instead of Component's.
window.innerHeight👉 767
document.body.offsetHeight👉 767
But when I in chrome console:
console.log(document.getElementsByClassName('home-container')[0].clientHeight)
> 1484
Which the 'home-container' is a wrapped component:
withInfiniteScroll(HomeContainer);
Wrapped component should either expose a ref to underlying DOM element with forwardRef:
function withInfiniteScroll(Component) {
return class extends React.Component {
ref = React.createRef();
componentDidMount() {
window.addEventListener('scroll', this.onScroll, true);
}
onScroll = () => {
console.log(this.ref.current.clientHeight);
}
render() {
return <Component ref={this.ref} {...this.props} />;
}
};
}
const Foo = React.forwardRef((props, ref) => (
<div ref={ref}>Foo</div>
));
const FooWithScroll = withInfiniteScroll(Foo);
Or wrapper component should add container DOM element:
function withInfiniteScroll(Component) {
return class extends React.Component {
// ...same as above
render() {
return <div ref={this.ref}><Component {...this.props} /></div>
}
};
}

Array JSON with ListView in React-Native

I have an issue, I'm trying to make a little database offline inside a JSON, like this:
[
{
title: "Carros",
carros: [
{
nome: "Ferrari"
}
]
},
{
title: "Motos",
carros: [
{
nome: "Suzuki"
}
]
}
];
From now, my HomeScreen lists the categories as "Carros" and "Motos", but when I want to enter in subtopic like "carros", but I can't.
Currently using a ListView
{ list.map((item, i) => (
<View>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("Listagem", {
itemName: item.title
})
}
>
<ListItem key={item.title} title={item.title} />
</TouchableOpacity>
</View>
))
}
How to get child items?
Carros -> carros
Motos -> carros ???
Motos -> motos
If you only have "carros", there are no "motos". Hope you get my point.
In order to get the carros Object inside the Listagem screen you need to pass it as an prop.
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("Listagem", {
itemName: item.title,
childItems: item.carros
})
}
>
In you Listagem screen you will get these properties.
constructor() {
super();
const { navigation } = this.props;
const itemName = navigation.getParam('itemName', 'defaultName');
const childItems = navigation.getParam('childItems', []);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(childItems),
};
}
render(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData.nome}</Text>}
/>
)
}
You can do this in your home screen:
import React, { Component } from 'react';
import {FlatList, ScrollView} from 'react-native';
import { List, ListItem } from 'react-native-elements'; //this is not necessary. You may use it for aesthetic purposes only.
const cars = [
{
title: "Carros",
carros: [
{
nome: "Ferrari"
}
]
},
{
title: "Motos",
carros: [
{
nome: "Suzuki"
}
]
}
];
export default class Home extends Component {
renderCars() {
return (
<List
containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}
>
<FlatList
data={cars}
keyExtractor={item => item.title}
renderItem={({ item }) => (
<ListItem
titleStyle={yourstyles.title}
hideChevron
title={item.title}
onPress={() => this.props.navigation.navigate(
'ChildPage',
{
cars: item //pass the entire item as a prop. This way you will have all its subtopics in your child page
}
)}
/>
)}
/>
</List>
)
}
render() {
return (
<ScrollView>
{this.renderCars()}
</ScrollView>
)
}
}
In your child page where you want to list the subtopics of 'carros' for example. Remember you passed the entire item as a prop, so that prop will be available for you in the child page.
So now you can do this in your child page:
//ChildPage
import React, { Component } from 'react';
import {FlatList, ScrollView, View, Text} from 'react-native';
import { List, ListItem } from 'react-native-elements'; //this is not necessary. You may use it for aesthetic purposes only.
export default class ChildPage extends Component {
renderSubtopics() {
const { cars } = this.props.navigation.state.params; // remember the entire item you passed down from Home, we will use it here
return (
<List
containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}
>
<FlatList
data={cars.carros}
keyExtractor={item => item.nome}
renderItem={({ item }) => (
<ListItem
titleStyle={yourstyles.title}
hideChevron
title={item.nome}
onPress={() => //do whatever )}
/>
)}
/>
</List>
)
}
render() {
return (
<ScrollView>
<View>
<Text>
{this.props.navigation.state.params.cars.title}
<Text>
</View>
{this.renderSubtopics()}
</ScrollView>
)
}
}

How to call image from JSON that has local path in React Native

import React, { Component } from 'react';
import {
ScrollView,
StyleSheet,
} from 'react-native';
import axios from 'axios';
import CarDetail from '../components/CarDetail';
class CarList extends Component {
state = { cars: [] };
componentDidMount() {
axios.get('https://www.website.com/ajx/home_ajx/lister')
.then(response => this.setState({ cars: response.data.data[0]['S1']
}));
//Add an if statement here to notify the user when the request fails
}
renderCars() {
return this.state.cars.map(model =>
<CarDetail key={model.title} modelprop={model} />
);
}
render() {
console.log(this.state);
return (
<ScrollView>
{this.renderCars()}
</ScrollView>
);
}
}
export default CarList;
And the image path in the JSON file is as below:
"image": "data/models/peugeot-2008-138twf_600X400_.jpg",
Below is where I'm calling the image in another component
const CarDetail = ({ modelprop }) => {
const { image } = modelprop;
return (
<Card>
<CardSection>
<View>
<Image style={imageStyle}
source={{ uri: props.modelprop.image }}
/>
</View>
I believe I need to use some kind of prefix maybe in my Global.js which I couldn't find or figure out.
Any help is highly appreciated.
Mostly like your code has a bug:
const CarDetail = ({ modelprop }) => {
const { image } = modelprop;
return (
<View>
<Image
style={imageStyle}
source={{ uri: image }}
/>
</View>
);
}
If the image is something like data/models/peugeot-2008-138twf_600X400_.jpg, you should use `${Global.imageUrlPrefix}${image}` to concat all.