losing my mind - why doesn't my fetch re render (redux) - react-native

In my project I have many users and many resources (and many user_resources/the join between users and resources).When I POST to user_resources I see it work on my rails backend (as in I see that instance posted) but in my react native front end I don't see it listed upon update. However, once the app is completely refresh (when I stop and restart the expo server), I finally see those items rendered. ANY IDEAS? I've been working on this forever now to no avail and my project is due tmrw, so any help is appreciated.
screen where I post to user_resources:
import React from 'react';
import { ScrollView,SafeAreaView,StyleSheet, Text, View, FlatList, TouchableOpacity,Button, NativeEventEmitter} from 'react-native';
import {connect} from 'react-redux';
import {fetchResources,searchChanged} from '../actions';
import { addUserResource } from '../actions'
import {SearchBar} from 'react-native-elements';
import { MaterialIcons } from '#expo/vector-icons';
import { MaterialCommunityIcons } from '#expo/vector-icons';
class ResourcesScreen extends React.Component {
state = {
search: ''
}
componentDidMount = () =>{
this.props.fetchResources();
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "lightblue",
}}
/>
);
}
handlePress(item) {
debugger
fetch('http://localhost:3000/user_resources', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify({
resource_id: item.id,
user_id: this.props.users.id,
name: item.name,
description:item.description,
link:item.link,
})
})
.then(res => res.json())
.then(data2 => {
console.log(data2)
this.props.addUserResource(data2)
console.log(this.props)
})
}
header = () => {
return <View>
<Text style={styles.header}>Resources</Text>
</View>
}
onSearchChange = text => {
this.setState({search:text})
}
render(){
return(
<SafeAreaView>
<SearchBar placeholderTextColor="white" placeholder="Enter resource name here" onChangeText={this.onSearchChange} value={this.state.search}/>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Add A New Resource',{topicId:this.props.route.params.topicId})} style={styles.buttonitem}>
<Text style={styles.text}>
<MaterialIcons name="add-circle-outline" size={24} color="white"/>Add A New Resource
</Text>
</TouchableOpacity>
<FlatList keyExtractor={(item)=> item.id.toString()} data={this.props.resourceName} ItemSeparatorComponent = { this.FlatListItemSeparator } renderItem={({item}) => {
return <TouchableOpacity style={styles.material2}>
<Text onPress={() => this.props.navigation.navigate('Add A New Resource',{topicId:item.id})} style={styles.listitem}>{item.name}</Text>
<MaterialCommunityIcons name="bookmark-plus" size={50} color="#16a085" backgroundColor='black' onPress={()=>this.handlePress(item)}/>
</TouchableOpacity>
}}
ListHeaderComponent = {this.header}/>
</SafeAreaView>
)
}
}
const mapStateToProps = (state) => {
return {
resourceName: state.resourceReducer.resources,
users: state.userReducer,
search:state.resourceReducer.searchTerm
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchResources: () => dispatch(fetchResources()),
addUserResource,
searchChanged
}
}
export default connect(mapStateToProps,mapDispatchToProps)(ResourcesScreen)
After this I head to the profile page where the user_resources SHOULD be displayed, but aren't
import React from 'react';
import { ScrollView,StyleSheet, Text, View, FlatList, TouchableOpacity} from 'react-native';
import {connect} from 'react-redux';
import {SearchBar} from 'react-native-elements';
import { AntDesign } from '#expo/vector-icons';
class Profile extends React.Component{
handleDelete = (id) => {
debugger
fetch(`http://localhost:3000/user_resources/${id}`, {
method: "DELETE",
headers: {
"Authorization": this.props.users.token
}
})
.then(r => r.json())
.then((delResource) => {
console.log(delResource)
this.props.deleteOneFood(delResource)
console.log('deleted')
this.forceUpdate()
})
}
render(){
return(
<View>
{this.props.users.user_resources.map(singleResource=> {
return <Text key={singleResource.id}>{singleResource.name}</Text>
})}
</View>
)}
}
let deleteOneResource = (id) => {
return {
type: "DELETE_ONE_USER_RESOURCE",
payload: id
}
}
const mapDispatchToProps = {
deleteOneResource
}
const mapStateToProps = (state) => {
return {
users: state.userReducer,
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Profile)
I had a flatlist before but thought that may be causing the issues so rendered it another way, still no luck. I tried forceUpdates as well, still no luck. I'm not sure if the issue is coming from my reducer:
let userInitialState = {
id: 0,
username: "",
name: '',
category: '',
token: "",
user_resources:[],
}
let userReducer = (state = userInitialState, action) => {
switch(action.type){
case "ADD_USERS":
let singleNestedObject = {
...action.users.user,
token: action.users.token
}
return {
...state,
username: action.users.user.username,
token: action.users.token,
id: action.users.user.id,
name: action.users.user.name,
category: action.users.user.category,
user_resources: action.users.user.user_resources
}
case "ADD_ONE_USER_RESOURCE":
let copyOfResources = [...state.user_resources, action.userResources]
return {
...state,
user_resources: copyOfResources
}
default:
return state
}
}
and it's action
export const addUserResource = (resourceInfo) => {
return {
type: "ADD_ONE_USER_RESOURCE",
userResources: resourceInfo
}
}
Please help me find the issue here, I'm losing it.

Related

Tour view does not have React internal Fiber

I want implementation app-tour in my project , I installed react-native-app-tour .I have a View that I want assign app-tour to it and I use ref in view that it dispatch AppTourView to inputreducer and finally in useEffect I run appTour but when I run app I encountered Error that error is Tour view does not have React internal Fiber, please can help me
import React, {
useState,
useEffect,
useCallback,
useReducer,
useRef,
} from 'react';
import {AppTour, AppTourSequence, AppTourView} from 'react-native-app-tour';
const inputReducer = (state, action) => {
if (action.type == 'SET_APPTOUR') {
return [...state.appTour, action.data];
}
};
export default function HomeScreen(props) {
const [forsat, setForSat] = useState(null);
const [appTourTargets, dispatchAppTour] = useReducer(inputReducer, {
appTour: [],
});
useEffect(() => {
setTimeout(() => {
console.log(appTourTargets);
let appTourSequence = new AppTourSequence();
appTourTargets.appTour.forEach(appTourTarget => {
appTourSequence.add(appTourTarget);
});
AppTour.ShowSequence(appTourSequence);
}, 1000);
}, [appTourTargets]);
return(
<view style={styles.container}>
<View
ref={useCallback(ref => {
if (!ref) return;
setForSat(ref);
let prop = {
order: 12,
title: 'This is a target View',
description: 'We have the best targets, believe me',
outerCircleColor: '#3f52ae',
cancelable: false,
};
dispatchAppTour({type:'SET_APPTOUR', data:AppTourView.for(ref, {...prop})});
}, [])}
style={styles.rankContainer}>
<BText font="_Light" size={15} color={color.firstColor}>
فرصت{' '}
<BText font="_Bold" color={color.secondColor} size={15}>
{FormatHelper.toPersianString(daysToGo.toString())}
</BText>{' '}
روز تا کنکور
</BText>
</View>
</view>
)
}

Why isn't flatlist not able to map through and display the data?

I am using Zomato API to get the list of restaurants, data is in the form of the array which has object restaurants in which there are different restaurant objects and then their name and other details.
It's my first time using a flat List and I am not able to display this data.
Goal: Use the search bar to display the list of restaurants in the city using flatlist.
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { SearchBar } from 'react-native-elements';
import { FlatList } from 'react-native-gesture-handler';
class Main extends Component {
constructor(props) {
super(props);
this.state = { search: '', data: [] };
}
componentDidMount() {
fetch('https://developers.zomato.com/api/v2.1/search', {
method: 'GET',
headers: {
'user-key': '999999999999999999999999999'
},
params: JSON.stringify({
entity_type: 'city',
q: {this.state.search}
}),
}).then((response) => response.json())
.then((responseJson) => { return this.setState({ data: responseJson.restaurants }) })
.catch((error) => { console.warn(error) }
);
}
render() {
let data = this.state.data;
let name = data.map(p => p.restaurant.name)
console.warn("Check Data", name)
return (
<View>
<SearchBar
round
searchIcon={{ size: 24 }}
placeholder='Search'
onChangeText={search => { this.setState({ search }) }}
value={this.state.search}
/>
//Using this method to display the data if any
{name.length != 0 ?
<FlatList
data={name}
keyExtractor={(x, i) => x + i}
renderItem={({ name }) => <Text>{name}-DATA</Text>}
/>
: <View style={{height:"100%", width:"100%", alignItems:"center",
justifyContent:"center"}}>
<Text>No data found</Text>
</View>
}
</View>
);
}
}
export default Main;
Maybe the way I declared state is wrong, or maybe the way I'm storing the data in the state is wrong.
I got the names of the restaurant in the console.warn successfully.
Without your users-key I can't surely understand what is your api results.
Here
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { SearchBar } from 'react-native-elements';
import { FlatList } from 'react-native-gesture-handler';
class Main extends Component {
constructor(props) {
super(props);
this.state = { search: '', data: [] };
}
componentDidMount() {
fetch('http://phplaravel-325095-1114213.cloudwaysapps.com/api/shop/shop/', {
method: 'GET',
}).then((response) => response.json())
.then((responseJson) => { return this.setState({ data: responseJson }) })
.catch((error) => { alert(error) }
);
}
render() {
let data = this.state.data;
return (
<View>
<SearchBar
round
searchIcon={{ size: 24 }}
placeholder='Search'
onChangeText={search => { this.setState({ search }) }}
value={this.state.search}
/>
{data.length != 0 ?
<FlatList
data={data}
keyExtractor={(x, i) => x + i}
renderItem={({ item }) => <Text>{item.name}-DATA</Text>}
/>
: <View style={{height:"100%", width:"100%", alignItems:"center",
justifyContent:"center"}}>
<Text>No data found</Text>
</View>
}
</View>
);
}
}
export default Main;
This is a working code with another api call just add your api call instead on mine.. This is working properly. I guess you are just messing with your data.
Try replacing the renderItem from FlatList to
renderItem={({ item }) => <Text>{item}-DATA</Text>}
Also, replace the condition to use double equals like name.length !== 0
Check the url link properly.
https://developers.zomato.com/api/v2.1/search/999999999999999999999999999
Just check it on web browser it is showing message : The requested url was not found
It means we are not getting any data from this URL.
That why we are not able to map any data.

Lodash debounce not working all of a sudden?

I'm using a component I wrote for one app, in a newer app. The code is like 99% identical between the first app, which is working, and the second app. Everything is fine except that debounce is not activating in the new app. What am I doing wrong?
// #flow
import type { Location } from "../redux/reducers/locationReducer";
import * as React from "react";
import { Text, TextInput, View, TouchableOpacity } from "react-native";
import { Input } from "react-native-elements";
import { GoogleMapsApiKey } from "../../.secrets";
import _, { debounce } from "lodash";
import { connect } from "react-redux";
import { setCurrentRegion } from "../redux/actions/locationActions";
export class AutoFillMapSearch extends React.Component<Props, State> {
textInput: ?TextInput;
state: State = {
address: "",
addressPredictions: [],
showPredictions: false
};
async handleAddressChange() {
console.log("handleAddressChange");
const url = `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${GoogleMapsApiKey}&input=${this.state.address}`;
try {
const result = await fetch(url);
const json = await result.json();
if (json.error_message) throw Error(json.error_message);
this.setState({
addressPredictions: json.predictions,
showPredictions: true
});
// debugger;
} catch (err) {
console.warn(err);
}
}
onChangeText = async (address: string) => {
await this.setState({ address });
console.log("onChangeText");
debounce(this.handleAddressChange.bind(this), 800); // console.log(debounce) confirms that the function is importing correctly.
};
render() {
const predictions = this.state.addressPredictions.map(prediction => (
<TouchableOpacity
style={styles.prediction}
key={prediction.id}
onPress={() => {
this.props.beforeOnPress();
this.onPredictionSelect(prediction);
}}
>
<Text style={text.prediction}>{prediction.description}</Text>
</TouchableOpacity>
));
return (
<View>
<TextInput
ref={ref => (this.textInput = ref)}
onChangeText={this.onChangeText}
value={this.state.address}
style={[styles.input, this.props.style]}
placeholder={"Search"}
autoCorrect={false}
clearButtonMode={"while-editing"}
onBlur={() => {
this.setState({ showPredictions: false });
}}
/>
{this.state.showPredictions && (
<View style={styles.predictionsContainer}>{predictions}</View>
)}
</View>
);
}
}
export default connect(
null,
{ setCurrentRegion }
)(AutoFillMapSearch);
I noticed that the difference in the code was that the older app called handleAddressChange as a second argument to setState. Flow was complaining about this in the new app so I thought async/awaiting setState would work the same way.
So changing it to this works fine (with no flow complaints for some reason. maybe because I've since installed flow-typed lodash. God I love flow-typed!):
onChangeText = async (address: string) => {
this.setState(
{ address },
_.debounce(this.handleAddressChange.bind(this), 800)
);
};

React native - Can't dispatch action in component because state gets undefined

In my react native android app, when I try to dispatch an action in BoardsScreen or in the root of the app, the following error pops up:
However, when I remove it, the app doesn't crashes.
BoardsScreen.js
import React from 'react';
import { connect } from 'react-redux';
import { Container, Content, Text, List, Button, Icon, ListItem } from 'native-base';
import { ListView, StatusBar } from 'react-native';
import { ConfirmDialog } from 'react-native-simple-dialogs';
import ActionButton from 'react-native-action-button';
import { removeBoard } from '../actions/configurationActions';
class BoardsScreen extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
boardDeleteDialog: false,
secId: null,
rowId: null,
rowMap: null,
};
}
deleteRow(secId, rowId, rowMap) {
rowMap[`${secId}${rowId}`].props.closeRow();
const newData = [...this.props.boards];
newData.splice(rowId, 1);
this.props.removeBoard(newData);
this.setState({
rowId: null,
secId: null,
rowMap: null,
boardDeleteDialog: false,
});
}
dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
render() {
console.log(this.props.boards);
return (
<Container>
<StatusBar backgroundColor="#00C853" />
<ConfirmDialog
title="Delete board?"
animationType="fade"
visible={this.state.boardDeleteDialog}
positiveButton={{
title: 'Delete',
titleStyle: {
color: '#2ecc71',
},
onPress: () => this.deleteRow(this.state.secId, this.state.rowId, this.state.rowMap),
}}
negativeButton={{
titleStyle: {
color: '#2ecc71',
},
title: 'Cancel',
onPress: () =>
this.setState({
boardDeleteDialog: false,
secId: null,
rowId: null,
rowMap: null,
}),
}}
/>
<Content>
{this.props.boards.length >= 1 ? (
<List
style={{ backgroundColor: '#D9534F' }}
dataSource={this.dataSource.cloneWithRows(this.props.boards)}
renderRow={data => (
<ListItem
style={{ paddingLeft: 14, backgroundColor: 'transparent' }}
button
onPress={() =>
this.props.navigation.navigate('Board', {
board: data.board,
boardName: data.boardName,
})
}
>
<Text>{data.boardName}</Text>
</ListItem>
)}
renderRightHiddenRow={(data, secId, rowId, rowMap) => (
<Button
full
danger
onPress={() =>
this.setState({
boardDeleteDialog: true,
secId,
rowId,
rowMap,
})
}
>
<Icon active name="trash" />
</Button>
)}
disableRightSwipe
rightOpenValue={-75}
/>
) : (
<Text>No boards added.</Text>
)}
</Content>
<ActionButton
buttonColor="#2ecc71"
fixNativeFeedbackRadius
onPress={() => this.props.navigation.navigate('AddBoard')}
/>
</Container>
);
}
}
const mapStateToProps = state => ({
boards: state.configurationReducer.boards,
});
const mapDispatchToProps = dispatch => ({
removeBoard: (board) => {
dispatch(removeBoard(board));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(BoardsScreen);
App.js
import React from 'react';
import { connect } from 'react-redux';
import MainNavigator from './src/config/Router';
import { addBoardToList } from './src/actions/configurationActions';
import { Board } from './src/API';
class App extends React.PureComponent {
componentDidMount() {
Board.getList(true).then(response => this.parseDataFromJSONResponse(response));
}
parseDataFromJSONResponse(response) {
for (let i = 0; i < response.length; i += 1) {
this.props.addBoardToList(response[1]);
}
}
render() {
return <MainNavigator />;
}
}
const mapStateToProps = state => ({
boards: state.configurationReducer.boards,
});
const mapDispatchToProps = dispatch => ({
addBoardToList: (board) => {
dispatch(addBoardToList(board));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
configurationReducer.js
const initialState = {
theme: 1,
obscure: false,
boards: [],
boardsList: [],
};
const configurationReducer = (state = initialState, action) => {
let newState = { ...state };
switch (action.type) {
case 'ADD_BOARD':
newState = {
boards: [...state.boards, action.payload],
};
return newState;
case 'REMOVE_BOARD':
newState = {
boards: action.payload,
};
return newState;
case 'ADD_BOARD_TO_LIST':
newState = {
boardsList: [...state.boardsList, action.payload],
};
return newState;
default:
return state;
}
};
export default configurationReducer;
configurationActions.js
function addBoard(board) {
return {
type: 'ADD_BOARD',
payload: board,
};
}
function removeBoard(board) {
return {
type: 'REMOVE_BOARD',
payload: board,
};
}
function addBoardToList(board) {
return {
type: 'ADD_BOARD_TO_LIST',
payload: board,
};
}
export { addBoard, removeBoard, addBoardToList };
I really don't have a clue what is causing this, maybe it's a bug but I don't know if is react-redux fault or react native itself.
When you remove the board, it looks like in you reducer, you return a strange new state:
case 'REMOVE_BOARD':
newState = {
boards: action.payload,
};
return newState;
Should the boards to be an array always? I think you missed something, for example:
boards: state.boards.filter ((it) => it.id !== action.payload.id),

Why isn't mailchimp API working with fetch?

I'm trying to add an email address to a mailchimp list I have.
This is for a react native app and I'm trying to implement the request using fetch.
This is my code within the component:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import { emailChanged, nameChanged, addToWaitingList } from '../actions';
import { Card, CardSection, Input, Button, Spinner } from '../components/Auth';
class addToWaitingListForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
onButtonPress() {
const { email } = this.props;
this.props.addToWaitingList({ email });
}
renderButton() {
if (this.props.loading) {
return <Spinner size="large" />;
}
return (
<Button onPress={this.onButtonPress.bind(this)}>
Keep me in the loop!
</Button>
);
}
render() {
return (
<View>
<Card>
<CardSection>
<Input
placeholder="your name"
onChangeText={this.onNameChange.bind(this)}
value={this.props.name}
/>
</CardSection>
<CardSection>
<Input
placeholder="email#uni.ac.uk"
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
</CardSection>
<Text style={styles.errorTextStyle}>
{this.props.error}
</Text>
<CardSection style={{ borderBottomWidth: 0 }}>
{this.renderButton()}
</CardSection>
</Card>
</View>
);
}
}
const mapStateToProps = ({ auth }) => {
const { email, name, error, loading } = auth;
return { email, name, error, loading };
};
export default connect(mapStateToProps, {
emailChanged,
addToWaitingList
})(addToWaitingListForm);
Add this is my action code for interacting with the mailchimp api:
import Router from '../../navigation/Router';
import { getNavigationContext } from '../../navigation/NavigationContext';
export const addToWaitingList = ({ email }) => {
const emailListID = 'e100c8fe03';
fetch(`https://us13.api.mailchimp.com/3.0/lists/${emailListID}/members/`, {
method: 'POST',
body: JSON.stringify({
'email_address': email,
'status': 'subscribed',
'merge_fields': {
'FNAME': 'Urist',
'LNAME': 'McVankab'
}
})
})
.then(() => addSubscriberSuccess())
.catch(error => console.log(error));
};
const addSubscriberSuccess = () => {
getNavigationContext().getNavigator('root').immediatelyResetStack([Router.getRoute('auth')]);
};
Right now, the error I'm just getting back is ExceptionsManager.js:62 Cannot read property 'type' of undefined and Error: unsupported BodyInit type
What does this mean and how can I fix this?
You need to do two things.
First off you need to send the basic authentication via fetch so you cant do "user:pass" You have to convert it with btoa('user:pass').
Then you have to send it with mode: 'no-cors'
let authenticationString = btoa('randomstring:ap-keyxxxxxxx-us9');
authenticationString = "Basic " + authenticationString;
fetch('https://us9.api.mailchimp.com/3.0/lists/111111/members', {
mode: 'no-cors',
method: 'POST',
headers: {
'authorization': authenticationString,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email_address: "dude#gmail.com",
status: "subscribed",
})
}).then(function(e){
console.log("fetch finished")
}).catch(function(e){
console.log("fetch error");
})