Not able to use map function react-native - arrays

I am new to react native and I am using Async storage to store objects and then parse, by storing them in favorites array. I am trying to map each element in array and display it.
I get this error: this.state.favorites.map is not a function.
I think it's a state error but as I'm new to these concepts, i don't know how to fix it. Please help.
import React, {Component} from 'react';
import {StyleSheet, Text, View, Image, FlatList, Button, AsyncStorage,
TouchableOpacity} from 'react-native';
import ajax from './ajax.js';
import PropTypes from 'prop-types';
import InspirationList from './InspirationList';
import FavoriteItem from './FavoriteItem';
import ProductItem from './ProductItem';
class Favorites extends Component{
state = {
favorites:[],
};
componentDidMount(){
this.showProduct();
}
async showProduct() {
let k=0;
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiGet(keys)
.then((result) => {
result.map(req => req.forEach((element) => {
k++;
if(element!=null && k%2==0){
this.setState({favorites: JSON.parse(element) });
console.log(this.state.favorites.nume);
}
}));
}));
};
render(){
return(
<View style='styles.fav'>
{this.state.favorites.map((fav)=>
<Text>{fav.nume}</Text>
)}
</View>
);
}
}
const styles = StyleSheet.create({
fav:{
backgroundColor:'#999',
flex:1,
width:'100%',
paddingTop:150,
}
});
export default Favorites;

On your showProduct function you are overriding what your state originally was. When assigning the JSON.parse(element) you are changing your state instead of having an array your are converting it into an object, that's why you are getting that error. map exists only on array-like.
So maybe you can do something like:
async showProduct() {
let k=0;
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiGet(keys)
.then((result) => {
result.map(req => {
let result = [];
req.forEach((element) => {
k++;
if(element!=null && k%2==0){
result.push(JSON.parse(element))
}
});
this.setState({favorites: result });}
);
}));
};

Related

React Native : Rendered more hooks than during the previous render

I am getting this error after implementing the gethomeid useState and cant understand I tried different things but nothing worked
import React, {useEffect, useState} from 'react';
import {
StyleSheet,
View,
Image,
Text,
TouchableOpacity,
Button,
} from 'react-native';
import {windowWidth, windowHeight} from '../utils/Dimentions';
//components
import EmptyContainer from '../components/EmptyContainer';
import Header from '../components/Header';
import TopTabs from '../components/Tabs';
//redux
import {getHomeData} from '../action/homedata';
import {connect} from 'react-redux';
import propTypes from 'prop-types';
// to render empty container
const Home = ({getHomeData, homeState, userDetails}) => {
const [gethomeid, setGethomeid] = useState(null);
if (userDetails) {
useEffect(() => {
getHomeData('123456');
}, []);
}
if (homeState.data != null && userDetails && gethomeid != null) {
console.log(homeState.data);
return (
<View style={{flex: 1}}>
<Header username={userDetails.name.split(' ')[0]} />
<TopTabs roomdata={homeState.data.roomMapper} />
</View>
);
} else {
return (
<View>
<EmptyContainer />
</View>
);
}
};
const mapStateToProps = state => ({
homeState: state.data,
userDetails: state.auth.user,
});
const mapDispatchToProps = {
getHomeData,
};
Home.propTypes = {
getHomeData: propTypes.func.isRequired,
homeState: propTypes.object.isRequired,
userDetails: propTypes.object,
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I am trying to load 2 buttons that will pass the data on clicked to the gethomeid but this error i am not able to solve
THE SNAPSHOT OF THE ERROR:-
You are conditionally calling the useEffect hook. This breaks the rules of hooks. You can likely move the conditional into the hook callback.
useEffect(() => {
if (userDetails) {
getHomeData('123456');
}
}, []);
You should probably also add any missing dependencies the linter may complain about, like userDetails and getHomeData.
useEffect(() => {
if (userDetails) {
getHomeData('123456');
}
}, [getHomeData, userDetails]);

How to fix memory leak issue in react native?

Problem:
My App component is giving me an error like this.
D:\projects\myapp\node_modules\react-native\Libraries\Core\ExceptionsManager.js:173 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in App (at renderApplication.js:45)
This is how my code looks like.
/* eslint-disable prettier/prettier */
import React, {Component} from 'react';
import {StatusBar} from 'react-native';
import {Provider} from 'react-redux';
import RootNavigator from '_navigations/RootNavigator';
import AsyncStorage from '#react-native-community/async-storage';
import store from '_store';
import Splashscreen from '_screens/splashscreen';
import axios from 'axios';
import refreshToken from '_store/actions/refreshToken';
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
this.updateLoading();
refreshToken();
}
updateLoading = async () => {
const data = await this.performTimeConsumingTask();
if (data !== null) {
this.setState({isLoading: false});
}
};
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve('result');
}, 3000),
);
};
render() {
if (this.state.isLoading) {
return <Splashscreen />;
}
return (
<>
<StatusBar barStyle="dark-content" />
<Provider store={store}>
<RootNavigator />
</Provider>
</>
);
}
}
export default App;
My splash screen is like this.
import React from 'react';
import {View, Image} from 'react-native';
import styles from './splashstyle';
import logo from '_assets/img/logo.png';
const Splashscreen = () => {
return (
<View style={styles.container}>
<Image source={logo} style={styles.logo} />
</View>
);
};
export default Splashscreen;
As it is saying the issue is inside rendered method but I do not where I have done wrong. I tried a lot to find out what where I have done wrong but it is giving me this issue over and over. Can someone help me to solve this issue?Thank you
if performTimeConsumingTask take so much time that the user moved to another screen(aka component) then the function updateLoading will continue its process which will try to setState in an unmounted component..
so you should cancel your work in componentWillUnmount to fix the issue
I would suggest some tweak like
class MyComponent{
constructor(){ ... this.isMounted = false; }
componentDidMount(){ ... this.isMounted = true; }
componentWillUnmount() { ... this.isMounted = false }
// then inside updateLoading
updateLoading = async () => {
const data = await this.performTimeConsumingTask();
if (this.isMounted && data !== null) {
// will protect set state against unmounted component
this.setState({isLoading: false});
}
};
}

React Native save change switch with Async Storage

Im new in React Native, I have a problem with Switch, I want to save changes, dark mode and Switch, when I turn off the app and come back my changes should be saved. When I close the app, my switch came back to first position and dark mode does not work. I know that Im doing something wrong, but I did not mobile app and this is my first time and I dont know how to use AsyncStorage in this App to work this. Can somebody help me solve this problem?
import React, { createContext, useState, useEffect } from 'react';
import { AsyncStorage } from 'react-native';
export const DarkModeContext = createContext();
export default function DarkModeContextProvider(props) {
const [switchMode, setSwitchMode] = useState(false);
useEffect(() => {
let switch1 = switchMode;
AsyncStorage.setItem('switch1', JSON.stringify(switch1));
});
const SwitchThis = () => {
setSwitchMode(!switchMode);
};
return (
<DarkModeContext.Provider
value={{
switchMode,
SwitchThis
}}
>
{props.children}
</DarkModeContext.Provider>
);
}
and next component:
import React, { useState, useContext } from 'react';
import { View, ScrollView, TouchableOpacity, Text, AsyncStorage } from 'react-native';
import { List } from 'react-native-paper';
import BackgroundImage from './BackgroundImage';
import Clock from './Clock';
import TabIcon from './TabIcon';
import AddButton from './AddButton';
import { DarkModeContext } from './app-context';
const HomeScreen = () => {
const { switchMode } = useContext(DarkModeContext);
displayData = async () => {
try {
let switch1 = await AsyncStorage.getItem('switch1', function (err, switch1) {
JSON.parse(switch1)
}
)
return switch1
}
catch (error) {
return error
}
}
return (
<View
style={{
flex: 1,
backgroundColor: !switchMode ? 'white' : '#353535'
}}
>
<BackgroundImage fabButton={<AddButton/>}>
<Clock />
</BackgroundImage>
<ScrollView>
<List.Section>
<List.Subheader style={{ color: !switchMode ? 'black' : 'white' }}>
Task List
</List.Subheader>
<TouchableOpacity onPress={displayData}>
<Text>Click displayData</Text>
</TouchableOpacity>
</List.Section>
</ScrollView>
</View>
);
};
You are importing AsyncStorage from 'react-native' which is deprecated
use #react-native-community/react-native-async-storage
npm i #react-native-community/react-native-async-storage
And on your home screen you are not calling the function displayData() so how is data supposed to be displayed without function call.
and i do suggest making separate functions for writing and reading from async storage, it will help you reduce your code and time.
Like this:
let storeData=(name, obj)=> {
return new Promise((resolve,reject)=>{
let jsonOfItem = JSON.stringify(obj)
AsyncStorage.setItem(name, jsonOfItem).then(r=>resolve(jsonOfItem))
.catch(e=>reject(e))
})
}
let readData=(name)=> {
return new Promise((resolve,reject)=>{
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value
AsyncStorage.getItem(name).then(r=> resolve(JSON.parse(r)) )
.catch(e=>reject(e))
})
}
//Now you can just read write easily in async function like this:
let fetchedData = await readData("key")
//and for storing data.
storeData("key",object)

FlatList in react native is rendering but not showing text

I'm trying to render a flatlist by connecting to a internal JSON file. The flatlist seems to be rendering but not showing any text. The cardlist in the code is being rendered 9 times, there are 9 objects in the JSON file. But no text is showing.
// 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() {
// console.log(this.props);
// return;
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;
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;
Just want to list the title at this stage.
You need to modify renderItem because FlatList passes an object into the renderItem callback function.
Instead, use the below
renderItem = ({ item }) => <ListItem library={item} />
Thanks Dan you put me onto the right lines. I used
renderItem={({ item }) => this.renderItem(item)}

React native infinite loop while pass the parameter into function

How to call function inside another function>
For example
I have 2 cascade dropdown which value from second dropdown depends on from first dropdown. inside the first dropdown there is a method like this
`onValueChange = {(value)=>{this.props.getSecondValue(value.id)}}.`
My question is why every time I change the value from first dropdown it causes infinite loop in this.props.getSecondValue() function?
How to solve that problem.
Many thanks !
update
here my snippet code
import React, {Component} from 'react';
import {StyleSheet, Text, View, TouchableOpacity, KeyboardAvoidingView, SafeAreaView, ScrollView} from 'react-native';
import {bindActionCreators} from "redux";
import {RootActions} from "../../shared/root-actions";
import connect from "react-redux/es/connect/connect";
import Config from 'react-native-config';
import _ from 'lodash';
import {Dropdown} from 'react-native-material-dropdown';
type Props = {};
const styles = StyleSheet.create({
container: {
flex: 1
}
});
class Index extends Component<Props> {
constructor(props) {
super(props);
this.state = {
point_type_id: '',
point_category_id: '',
}
this._getPointTypes = this._getPointTypes.bind(this);
this._getPointCategories = this._getPointCategories.bind(this);
}
componentDidMount() {
this._getPointTypes();
}
_getPointTypes(){
this.props.getPointTypes();
}
_getPointCategories(point_type_id){
this.props.getPointCategories(point_type_id);
}
render() {
let {
point_type_id,
point_category_id,
} = this.state;
let {
} = this.locationDetailStates;
return (
<View style={styles.container}>
....
<Dropdown
label='Point types'
data={point_types}
labelExtractor = {(value)=>{this._getPointCategories(value.id)}}
/>
<Dropdown
label='Point categories'
data={point_categories}
/>
</View>
);
}
}
function mapStateToProps(state) {
return {
outdoorStates: state.outdoorStates,
locationDetailStates:state.locationDetailStates
}
}
function mapDispatchToProp(dispatch) {
return bindActionCreators(RootActions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProp)(Index);
Try to refactor like this:
function mapDispatchToProps(dispatch) {
return {
getSecondValue: (id)=>{
//Your code here
}
}
}
class MyClass extends React.Component {
handleChange = (e)=>{
//assign your id from event values
this.props.getSecondValue(id);
}
...
render(){
return (
//inside a tag
onChange = {this.handleChange}
)
}
}

Resources