I'm fairly new to react native and redux and was trying to render the library title from a JSON file in a flat list using redux, but my FlatList component does not render anything on the screen.
here's my code :
LibraryList.js
import React, { Component } from "react";
import { FlatList } from "react-native";
import { connect } from "react-redux";
import ListItem from "./ListItem";
class LibraryList extends Component {
renderItem(library) {
return <ListItem library={library} />;
}
render() {
return (
<FlatList
data={this.props.libraries}
renderItem={this.renderItem}
keyExtractor={library => library.id}
/>
);
}
}
const mapStateToProps = state => {
return { libraries: state.libraries };
};
export default connect(mapStateToProps)(LibraryList);
ListItem.js
import React, { Component } from "react";
import { Text } from "react-native";
import { CardSection } from "./common";
class ListItem extends Component {
render() {
return (
<CardSection>
<Text>{this.props.library.title}</Text>
</CardSection>
);
}
}
export default ListItem;
App.js
import React from "react";
import { View } from "react-native";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./reducers";
import { Header } from "./components/common";
import LibraryList from "./components/LibraryList";
const App = () => {
return (
<Provider store={createStore(reducers)}>
<View>
<Header headerText="Tech Stack" />
<LibraryList />
</View>
</Provider>
);
};
export default App;
The JSON file is like
[
{
"id": '' ,
"title": '' ,
"description":''
},
{
"id":'' ,
"title":'' ,
"description":''
}
]
I read some solutions for this suggesting changing the renderItem function to something like this
renderItem = ({ library }) => <ListItem library={library} />
still does not work. Can someone help me with this problem?
Thanks.
You have to make your renderItem as an arrow function. Otherwise you have to bind your function inside constructor in order to access function as renderItem={this.renderItem}.
import React, { Component } from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import ListItem from './ListItem';
class LibraryList extends Component {
renderItem = ({ item }) => {
return <ListItem library={item} />
}
render() {
return (
<FlatList
data={this.props.libraries}
renderItem={this.renderItem}
keyExtractor={library => library.id}
/>
);
}
}
const mapStateToProps = state => {
return { libraries: state.libraries };
};
export default connect(mapStateToProps)(LibraryList);
or you can call your renderItem as an arrow function inside render like below
renderItem={(item) => this.renderItem(item)}
but using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
Hope this helps you. Feel free for doubts.
In your flatlist try thi s:
<FlatList
data={this.props.libraries}
renderItem={({item, index}) => {
this.renderItems(item); // change this name to renderItems so that it doesnt clash with flatlist default renderItem
}}
/>
Hope it helps. feel free for doubts
You have several approaches to your problem.
Firstly your renderItem should be binded, so either do this
renderItem = (library) => {
or this
renderItem={this.renderItem.bind(this)}
besides the binding problem, flatlist prop renderItem will return to your function an object with this structure
{ item, index }
so in reality your renderItem should be like this
renderItem({ item }){
return <ListItem library={item} />;
}
Related
I have two components. One is the provider and the second is a child. Now I want to use the function of provider in the child but with my current approach, it says that function is undefined. Can you see what I'm doing wrong?
Here is the code below.
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
const MyProvider = (props) => {
const { children } = props;
const handlePress = () => {
console.log("Provider component function called!");
};
return (
<View>
{children}
</View>
);
};
const NoLocationAccess = (props) => {
const { handlePress } = props;
console.log("handlePress : ",handlePress)
return (
<TouchableOpacity onPress={handlePress}>
<Text>I am the child component</Text>
</TouchableOpacity>
);
};
export default NoLocationAccess;
I have tried provider.wrapper. that made things more problematic.
To call a function, that is defined in the provider, from the child you need too pass it down as a prop.
Here the modified Code:
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
const MyProvider = (props) => {
const { children } = props;
const handlePress = () => {
console.log("Provider component function called!");
};
return (
<View>
{React.Children.map(children, (child) => {
return React.cloneElement(child, { handlePress });
})}
</View>
);
};
const NoLocationAccess = (props) => {
const { handlePress } = props;
console.log("handlePress : ",handlePress)
return (
<TouchableOpacity onPress={() => handlePress()}>
<Text>I am the child component</Text>
</TouchableOpacity>
);
};
export default NoLocationAccess;
Try it
It took me a while. but I have done this high-order component. this idea came to mind with the redux connect method. so took that approach and created a higher-order component. that works flawlessly.
Here is the solution.
High-Order Component.
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
const MyProvider = (props) => {
class GPSComponent extends React.Component {
componentDidMount() {
}
requestPermissions= async()=> {
console.log("i is called",this.props.userCurrentLocation)
}
render() {
return <WrappedComponent
requestPermissions={this.requestPermissions}
{...this}
/>;
}
}
return GPSComponent;
};
child component.
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import MyProvider from "./MyProvider"
const NoLocationAccess = (prop) => {
const { requestPermissions } = prop;
console.log("requestPermissions ",requestPermissions)
return (
<TouchableOpacity onPress={requestPermissions}>
<Text>I am the child component</Text>
</TouchableOpacity>
);
};
export default MyProvider(NoLocationAccess);
import { FlatGrid } from 'react-native-super-grid';
class Grid extends React.Component () {
export default function Example() {
I just write until here..
But, don't work...
What I have to do ..
You have to return a valid response from the component. For example, if you want to make a Component using FlatGrid you can use this structure:
import { View } from 'react-native';
import { FlatGrid } from 'react-native-super-grid';
export default function Example() {
return(
<View>
<FlatGrid
itemDimension={130}
data={[1,2,3,4,5,6]}
renderItem={({ item }) => (<Text>{item}</Text>)}
/>
</View>
)
}
And also I see you are using a class class named Grid that is extending the React.Component () and from your code I see you are not importing the React from react so in order to use the class you have to use:
import React from 'react';
import { View } from 'react-native';
import { FlatGrid } from 'react-native-super-grid';
export default class Grid extends React.Compoment {
render(){
return(
<View>
<FlatGrid
itemDimension={130}
data={[1,2,3,4,5,6]}
renderItem={({ item }) => (<Text>{item}</Text>)}
/>
</View>
)
}
}
I make a request with axios in a saga, which I receive in a component through the reduction in the mapStateToProps, if I go through the data in that component I can access any level but if I send it on props I can only access the first level of the json and on the second level I get Cannot read property 'route' of undefined
component where I trigger the action and receive the state, and I pass the props:
import React, { Component } from 'react';
import Aux from '../../hoc/Auxiliary';
import Products from '../../Components/ProductsSencillo/Products'
import { Grid } from 'styled-css-grid';
import { connect } from 'react-redux'
import { productComplete } from '../../redux/actions/productAction'
import reducerProduct from '../../redux/modules/reducers/productReducer'
import { bindActionCreators } from 'redux';
class ProductBuilder extends Component {
componentDidMount() {
this.props.handleListar();
}
render() {
return (
<Aux>
<Grid columns="repeat(auto-fit,minmax(45px,1fr))">
<Products products={this.props.state} />
</Grid>
</Aux>
);
}
}
const mapStateToProps = (state) => ({
state: state.productReducer.productos.productos
})
const mapDispatchToProps = dispatch => ({
handleListar: bindActionCreators(productComplete, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(ProductBuilder);
component where I receive the props and the mappings:
import React, { Component } from 'react';
import Product from './Product/Product'
import { Cell } from 'styled-css-grid';
class Products extends Component {
render() {
return {
this.props.products.map(pro => {
return <Cell width={3}>< Product
image={pro.imagen_principal.ruta}
name={pro.nombre}
/>
</Cell>
})
}
}
}
export default Products;
error that throws me:
API:
When accessing "route" you have to first check if the value is present in the object that component receives.
Try this:
<Product image={pro.imagen_principal && pro.imagen_principal.ruta}/>
Hope this helps!
So I am trying to render my Firebase Database onto my Android simulator and it is showing an error, in which the solution is to convert an object into an array, but I do not know-how.
import _ from 'lodash';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {FlatList, View, Text} from 'react-native';
import {employeesFetch} from '../actions';
import ListItem from './ListItem';
class EmployeeList extends Component {
componentDidMount(){
this.props.employeesFetch();
}
renderRow(employee) {
return <ListItem employee={employee}/>;
};
render(){
console.log(this.props.employees);
return (
<View>
<FlatList
data={Object.keys(this.props.employees)}
renderItem={this.renderRow}
/>
</View>
);
}
}
const mapStateToProps = state => {
const employees = _.map(state.employees, (val, uid) => {
return { ...val, uid };
});
return { employees };
};
export default connect(mapStateToProps, { employeesFetch })(EmployeeList);
That mapStateToProps helper is supposed to create a variable called employees (if I am not wrong, I am just learning to code) but when I write that down like this:
<FlatList
data=employees
renderItem={this.renderRow}
/>
It says that employees is not defined. Just in case, I will add the ListItem file to show what I have done there.
import React, {Component} from 'react';
import {Text} from 'react-native';
import {CardSection} from './common';
class ListItem extends Component {
render () {
const { name } = this.props.employee;
return (
<CardSection>
<Text style={styles.titleStyle}>
{name}
</Text>
</CardSection>
);
}
}
export default ListItem;
I expect the output to be a list showing the employees (recorded in Firebase Database) on the screen, but is showing an error: Invariant Violation: Objects are not valid as a React child (found: object with keys {name, phone, shift, uid}). If you meant to render a collection of children, use an array instead.
This is my homepage.js
import _ from 'lodash';
import React, { Component } from 'react';
import { View, FlatList } from 'react-native';
import { connect } from 'react-redux';
import { fetchGames } from '../actions';
import GameRowItem from './GameRowItem';
class Homepage extends Component {
componentWillMount() {
this.props.fetchGames({this.props.userInfo});
}
renderRow(game) {
return <GameRowItem game={game} />;
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.props.games}
renderItem={this.renderRow}
keyExtractor={(game, index) => index.toString()}
/>
</View>
);
}
}
const mapStateToProps = state => {
const games = _.map(state.games, (val, uid) => {
return { ...val, uid };
});
return {
games,
userInfo: state.auth.userInfo
};
};
export default connect(mapStateToProps, { fetchGames })(Homepage);
I want to pass a prop to my action after connect() function runs. Because I need my application level state and if I can't reach them before componentWillMount() function runs, I can't fetch my games properly...
I know I told mixed everything and it is diffucult to understand. Even so I hope someone understand me.
If the props data is available when the component is initialized then you should better use componentDidMount.
If props data is available async after the initialization of the component, then you can use componentWillReceiveProps method which will be called every time whenever a prop is received from the parent component.