How to move function logic to reducer to update state - javascript

I have a prop in my file canPurchase that I want to update (toggle) using the reducer.
My application is working when the logic to update the prop canPurchase is in the same file however when I move the logic out into the reducer it does not work. What am I doing wrong?
burgerbuilder.js file:
class BurgerBuilder extends Component {
state = {
orderInProgress: false,
loading: false,
error: false
};
render() {
let burger = this.state.error ? (
<p>Ingredients can't be loaded </p>
) : (
<Spinner />
);
burger = (
<Aux>
<Burger ingredients={this.props.ings} />
<BuildControls
ingredientAdded={this.props.onIngredientAdded}
ingredientRemoved={this.props.onIngredientRemoved}
disabled={disabledInfo}
canPurchase={updatePurchaseState(this.props.ings)}
price={this.props.price}
ordered={this.orderInProgressHandler}
/>
</Aux>
);
}
return (
<Aux>
<Modal
show={this.state.orderInProgress}
modalClosed={this.orderCancelHandler}
/>
{burger}
</Aux>
);
}
}
/
const mapStateToProps = state => {
return {
ings: state.ingredients,
price: state.totalPrice,
canPurchase: state.canPurchase
};
};
const mapDispatchToProps = dispatch => {
return {
onIngredientAdded: ingNamePayload =>
dispatch({ type: actionTypes.ADD_INGREDIENT, payload: ingNamePayload }),
onIngredientRemoved: ingNamePayload =>
dispatch({ type: actionTypes.REMOVE_INGREDIENT, payload: ingNamePayload })
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withErrorHandler(BurgerBuilder, axios));
reducer.js
import PropTypes from "react";
const initialState = {
ingredients: {
salad: 0,
bacon: 0,
cheese: 0,
meat: 0
},
totalPrice: 0,
canPurchase: false
};
const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7
};
const updatePurchaseState = ingredients => {
const sum = Object.keys(ingredients)
.map(igKey => {
return ingredients[igKey];
})
.reduce((sum, el) => {
return sum + el;
}, 0);
return sum > 0;
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ADD_INGREDIENT:
return {
...state,
ingredients: {
...state.ingredients,
[action.payload]: state.ingredients[action.payload] + 1
},
totalPrice: state.totalPrice + INGREDIENT_PRICES[action.payload],
canPurchase: updatePurchaseState(state.ingredients)
};
default:
return state;
}
};
export default reducer;
the thing i am toggling
<button onClick={props.ordered} className={classes.OrderButton} disabled={!props.canPurchase}>
ORDER NOW
</button>`
if you need more information let me know.
thank you

{this.orderInProgressHandler} is being called as ordered props in BuildControls Component and used onClick button, where is it's definition?

Related

items not being displayed on table

I've been trying to create this search app where I can display the items in a table and delete items using react redux. However, on the initial load, the app shows a table but there is no data in the table. It's an empty table. If i search for another movie name which have more than one movie for that search term, then 2 tables would be shown but I want to show everything on the same table itself. The delete button is not working as well. Is there something wrong with my action and reducer files?
Action.js
import {
FETCH_MOVIE_PENDING,
FETCH_MOVIE_SUCCESS,
FETCH_MOVIE_ERROR,
DELETE_MOVIE
} from "./types";
const fetchMoviePendig = () => ({
type: FETCH_MOVIE_PENDING
});
const fetchMovieSuccess = json => ({
type: FETCH_MOVIE_SUCCESS,
payload: json
});
const fetchMovieError = error => ({
type: FETCH_MOVIE_ERROR,
payload: error
});
export const fetchMovie = name => {
return async dispatch => {
dispatch(fetchMoviePendig());
try {
const url = `https://jsonmock.hackerrank.com/api/movies/search/?Title=${name}`;
const response = await fetch(url);
const result = await response.json(response);
console.log(result);
dispatch(fetchMovieSuccess(result.data));
} catch (error) {
dispatch(fetchMovieError(error));
}
};
};
export const deleteEvent = id => async dispatch => {
try {
dispatch({
type: DELETE_MOVIE,
payload: id
});
} catch (err) {
console.log(err);
}
};
Reducer
import {
FETCH_MOVIE_PENDING,
FETCH_MOVIE_SUCCESS,
FETCH_MOVIE_ERROR,
DELETE_MOVIE
} from "../action/types";
const initialState = {
data: [],
loading: false,
error: ""
};
const moviesReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_MOVIE_PENDING:
return {
...state,
loading: true
};
case FETCH_MOVIE_SUCCESS:
return {
...state,
loading: false,
data: [...state.data, action.payload]
};
case FETCH_MOVIE_ERROR:
return {
...state,
loading: false,
error: action.payload
};
case DELETE_MOVIE:
return {
...state,
data: state.data.filter(movie => movie.id !== action.payload)
};
default:
return state;
}
};
export default moviesReducer;
App.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMovie } from "./action/movieActions";
import Input from "./components/Input";
import MovieTable from "./components/MovieTable";
class App extends Component {
state = {
searchInput: "The Rain"
};
componentDidMount() {
this.props.getMovieList(this.state.searchInput);
}
_getMovie = () => {
this.props.getMovieList(this.state.searchInput);
};
_onChangeHandler = e => {
this.setState({
searchInput: e.target.value
});
console.log(this.state.searchInput);
};
render() {
const { data, loading } = this.props.movies;
return (
<div className="center">
<div>
<h2 className="center white-text">Movie Search</h2>
</div>
<div className="container">
<Input
value={this.state.searchInput}
onChange={this._onChangeHandler}
onClick={this._getMovie}
/>
<div className="row">
{loading ? (
<p>Loading</p>
) : (
data.map(item => (
<MovieTable
key={item.imdbID}
year={item.Year}
name={item.Title}
movieId={item.imdbId}
/>
))
)}
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
movies: state.movies
};
};
const mapDispatchToProps = dispatch => {
return {
getMovieList: name => dispatch(fetchMovie(name))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Hello please take a look at the sandbox : https://codesandbox.io/s/prod-wind-4hgq2?file=/src/App.js
I have edited
<MovieTable
data={data.map(d => ({
year: d.Year,
name: d.Title,
movieId: d.imdbId
}))}
/>
and
case FETCH_MOVIE_SUCCESS:
return {
...state,
loading: false,
data: action.payload
};
And ... Currently the delete button has no event, that's why it can't work
I see data having the following pattern:
Object {page: 1, per_page: 10, total: 1, total_pages: 1, data: Array[1]}
page: 1
per_page: 10
total: 1
total_pages: 1
data: Array[1]
0: Object
Title: "Sin in the Rain"
Year: 2006
imdbID: "tt1072449"
And you are accessing wrong properties in the component render logic, can you fix that.
Duplicate table is created the way you have written the logic.
Pass the data to MovieTable component and let it render and create the table
and fill it.
In reducer (FETCH_MOVIE_SUCCESS) you need don't need to append data you have to
replace or use the current movie data only.

Item not deleting from array using Redux

I am following a tutorial trying to learn Redux. I got the first action working, which is a simple GET API call, but am stuck on the next action I'm trying to create. The code looks like the following:
In the Component:
class ShoppingList extends Component {
componentDidMount() {
this.props.getItems();
}
handleClick = id => {
console.log("component " + id);
this.props.deleteItem(id);
};
render() {
const { items } = this.props.item;
return (
<Container>
<ListGroup>
<TransitionGroup className="shoppingList">
{items.map(({ id, name }) => (
<CSSTransition key={id} timeout={500} classNames="fade">
<ListGroupItem>
<Button
className="button1"
color="danger"
size="sm"
onClick={e => this.handleClick(id, e)}
>
×
</Button>
{name}
</ListGroupItem>
</CSSTransition>
))}
</TransitionGroup>
</ListGroup>
</Container>
);
}
}
ShoppingList.propTypes = {
getItems: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
deleteItem: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
item: state.item
});
export default connect(mapStateToProps, { getItems, deleteItem })(ShoppingList);
In my reducer:
const initialState = {
items: [
{ id: 3, name: "Eggs" },
{ id: 4, name: "Milk" },
{ id: 5, name: "Steak" },
{ id: 6, name: "Water" }
]
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_ITEMS:
return {
...state
};
case DELETE_ITEM:
console.log("reducer");
return {
...state,
items: state.items.filter(item => item.id !== action.id)
};
default:
return state;
}
}
In my actions file:
export const getItems = () => {
return {
type: GET_ITEMS
};
};
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
payload: id
};
};
However, when I click on the button to try to delete an item from the list, nothing happens. I can see in the Redux console that the action is being dispatched, however it seems to have no effect. Any suggestions?
You have in deleteItem action { type, payload }. Instead you can have { type, id } or using payload in the reducer return statement.
I would do the following - so you are passing the id with the action instead of payload:
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
id
};
};
Or the best option for later purposes - keep payload just adding id as property:
// action
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
payload: { id }
};
};
// reducer
case DELETE_ITEM:
// here destructuring the property from payload
const { id } = action.payload;
return {
...state,
items: state.items.filter(item => item.id !== id)
};
I hope this helps!

My component is not updated when I am using Redux as a state manager?

This is just a sample code I am trying to control my controlled inputs using Redux, I add the Redux to my React project and add my reducer and action but everything works well except updating my component in one of my actions.
the following code is my Reducer:
import actionTypes from "./actions";
const uniqid = require("uniqid");
const firstID = uniqid();
const initialState = {
cons: [
{
value: "",
id: firstID,
added: false
}
],
pros: [
{
value: "",
id: firstID,
added: false
}
],
num: 0
};
const reducer = (state = initialState, action) => {
const newState = { ...state };
switch (action.type) {
case actionTypes.HANDLEINPUTCHANGE:
// const newState = state;
const changingItem = newState[action.case].find(item => {
return item.id === action.id;
});
const changingItemIndex = newState[action.case].findIndex(item => {
return item.id === action.id;
});
changingItem.value = action.event;
if (
changingItemIndex === newState[action.case].length - 1 &&
!changingItem.added
) {
alert(123);
const newItem = {
id: uniqid(),
value: "",
added: false
};
newState[action.case].push(newItem);
changingItem.added = true;
console.log(newState);
}
newState[action.case][changingItemIndex] = changingItem;
return newState;
case actionTypes.CLICK:
newState.num += 1;
return {
...newState
};
default:
return state;
}
};
export default reducer;
and the following code is my component, unfortunately, the HANDLEINPUTCHANGE action type did not update my component:
import React, { Component } from "react";
import FormElement from "../../base/components/formElement/FormElement";
import actionTypes from "../../base/store/actions";
import { connect } from "react-redux";
import "./style.scss";
class FormGenerator extends Component {
render() {
console.log(this.props);
return (
<ul className="row formGeneratorContainer fdiColumn">
<li onClick={this.props.click}>{this.props.num}</li>
{this.props[this.props.case].map((item, index) => {
return (
<li className="row formGeneratorItem" key={index}>
<div className="bullet d_flex jcCenter aiCenter">1</div>
{/* <FormElement onChange={(e,index,type,)}/> */}
<input
name={item.id}
type="text"
onChange={event =>
this.props.onFieldValueChange(
event.target.value,
index,
this.props.case,
item.id
)
}
/>
</li>
);
})}
</ul>
);
}
}
const mapStateToProps = state => {
return {
cons: state.cons,
pros: state.pros,
num: state.num
};
};
const mapDispachToProps = dispatch => {
return {
onFieldValueChange: (event, index, c, id) =>
dispatch({
event: event,
index: index,
case: c,
id: id,
type: actionTypes.HANDLEINPUTCHANGE
}),
click: () => dispatch({ type: actionTypes.CLICK })
};
};
export default connect(
mapStateToProps,
mapDispachToProps
)(FormGenerator);
You need to set value of your controlled component:
<input
name={item.id}
type="text"
value={item.value}
onChange={event =>
this.props.onFieldValueChange(
event.target.value,
index,
this.props.case,
item.id
)
}
/>
Other problems are in your reducer, you are mutating the redux state with these lines:
newState[action.case].push(newItem);
// ...
newState[action.case][changingItemIndex] = changingItem;
Look at these sections in the redux documentation:
Inserting and Removing Items in Arrays
Updating an Item in an Array

useEffect goes in inifinite loop with React Hooks

React/Redux application goes into an infinite loop on using useEffect with object references..
I am trying render pending todos for my application using useEffect.. and passing the array of todos as the second param in useEffect ..but why is not checking the values of the object ?
Container:
const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(RootActions, dispatch) });
const Home = (props) => {
const { root, actions } = props;
useEffect(() => {
getTodos(actions.loadPendingTodo);
}, [root.data]);
return (
<Segment>
<Error {...root } />
<TodoList { ...root } actions={actions} />
</Segment>
);
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Action:
export const loadPendingTodo = () => ({
type: LOAD_PENDING_TODO,
data: todoService.loadPendingTodo(),
});
Reducer:
const initialState = {
initial: true,
data: [{
id: 0,
text: 'temp todo',
dueDate: new Date(),
completedDate: '',
isDeleted: false,
isCompleted: false,
}],
error: false,
isLoading: false,
isEdit: false,
};
export default function root(state = initialState, action) {
switch (action.type) {
case LOAD_PENDING_TODO:
return {
...state,
data: [...action.data],
};
...
default:
return state;
}
}
getTodos Method:
export const getTodos = (loadTodo) => {
try {
loadTodo();
} catch (error) {
console.log(error); // eslint-disable-line
}
};
Service:
export default class TodoAppService {
loadPendingTodo() {
return store.get('todoApp').data.filter(todo => !todo.isCompleted && !todo.isDeleted);
}
Can anyone please help me out how to resolve this issue.. and there is no official documentation for this case too :/
Moreover changing the useEffect to the following works but i want to render on every change
useEffect(() => {
getTodos(actions.loadPendingTodo);
}, []);
Fixed it by removing the loadPedningTodo redux actions in useEffect that was causing it to loop and directly setting the data in function from service..
const Home = (props) => {
const { root, actions } = props;
return (
<Segment>
<Error {...root } />
<TodoList isEdit={root.isEdit} todo={todoService.loadPendingTodo()} actions={actions} />
</Segment>
);
};
thanks :)

React Native TypeError: TypeError: undefined is not an object (evaluating 'this.props.data.map')

I decide to reuse a component that I thought would work for my new application that is pulling in a third-party API.
The reusable component in question is iterating this.props.data.map() which is evaluating as being undefined in my components/Swipe.js file:
import React, { Component } from "react";
import {
View,
Animated,
PanResponder,
Dimensions,
LayoutAnimation,
UIManager
} from "react-native";
const SCREEN_WIDTH = Dimensions.get("window").width;
const SWIPE_THRESHOLD = 0.25 * SCREEN_WIDTH;
const SWIPE_OUT_DURATION = 250;
class Swipe extends Component {
static defaultProps = {
onSwipeRight: () => {},
onSwipeLeft: () => {}
};
constructor(props) {
super(props);
const position = new Animated.ValueXY();
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
onPanResponderMove: (event, gestureState) => {
position.setValue({ x: gestureState.dx, y: gestureState.dy });
},
onPanResponderRelease: (event, gestureState) => {
if (gestureState.dx > SWIPE_THRESHOLD) {
this.forceSwipe("right");
} else if (gestureState.dx < -SWIPE_THRESHOLD) {
this.forceSwipe("left");
} else {
this.resetPosition();
}
}
});
this.state = { panResponder, position, index: 0 };
}
componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.props.data) {
this.setState({ index: 0 });
}
}
componentWillUpdate() {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
LayoutAnimation.spring();
}
forceSwipe(direction) {
const x = direction === "right" ? SCREEN_WIDTH : -SCREEN_WIDTH;
Animated.timing(this.state.position, {
toValue: { x, y: 0 },
duration: SWIPE_OUT_DURATION
}).start(() => this.onSwipeComplete(direction));
}
onSwipeComplete(direction) {
const { onSwipeLeft, onSwipeRight, data } = this.props;
const item = data[this.state.index];
direction === "right" ? onSwipeRight(item) : onSwipeLeft(item);
this.state.position.setValue({ x: 0, y: 0 });
this.setState({ index: this.state.index + 1 });
}
resetPosition() {
Animated.spring(this.state.position, {
toValue: { x: 0, y: 0 }
}).start();
}
getCardStyle() {
const { position } = this.state;
const rotate = position.x.interpolate({
inputRange: [-SCREEN_WIDTH * 1.5, 0, SCREEN_WIDTH * 1.5],
outputRange: ["-120deg", "0deg", "120deg"]
});
return {
...position.getLayout(),
transform: [{ rotate }]
};
}
renderCards() {
console.log(this.props);
if (this.state.index >= this.props.data.length) {
return this.props.renderNoMoreCards();
}
return this.props.data
.map((item, i) => {
if (i < this.state.index) {
return null;
}
if (i === this.state.index) {
return (
<Animated.View
key={item[this.props.id]}
style={[this.getCardStyle(), styles.cardStyle]}
{...this.state.panResponder.panHandlers}
>
{this.props.renderCard(item)}
</Animated.View>
);
}
return (
<Animated.View
key={item[this.props.id]}
style={[styles.cardStyle, { top: 10 * (i - this.state.index) }]}
>
{this.props.renderCard(item)}
</Animated.View>
);
})
.reverse();
}
render() {
return <View>{this.renderCards()}</View>;
}
}
const styles = {
cardStyle: {
position: "absolute",
width: SCREEN_WIDTH
}
};
export default Swipe;
I am unclear why this is happening since I do get back a payload: data in my action creator:
export const fetchJobs = (region, callback) => async dispatch => {
try {
const url =
JOB_ROOT_URL +
JOB_QUERY_PARAMS.key +
"&method=" +
JOB_QUERY_PARAMS.method +
"&category=" +
JOB_QUERY_PARAMS.keyword +
"&format=" +
JOB_QUERY_PARAMS.format;
let { data } = await axios.get(url);
dispatch({ type: FETCH_JOBS, payload: data });
callback();
} catch (e) {
console.log(e);
}
};
So why is data evaluating as undefined in my reusable component?
It's being called here in DeckScreen.js:
import React, { Component } from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import { MapView } from "expo";
import { Card, Button } from "react-native-elements";
import Swipe from "../components/Swipe";
class DeckScreen extends Component {
renderCard(job) {
return (
<Card title={job.title}>
<View style={styles.detailWrapper}>
<Text>{job.company}</Text>
<Text>{job.post_date}</Text>
</View>
<Text>
{job.description.replace(/<span>/g, "").replace(/<\/span>/g, "")}
</Text>
</Card>
);
}
render() {
return (
<View>
<Swipe data={this.props.jobs} renderCard={this.renderCard} />
</View>
);
}
}
const styles = {
detailWrapper: {
flexDirection: "row",
justifyContent: "space-around",
marginBottom: 10
}
};
function mapStateToProps({ jobs }) {
return { jobs: jobs.listing };
}
export default connect(mapStateToProps)(DeckScreen);
The button I am pressing that gives me this error is in the MapScreen screen:
import React, { Component } from "react";
import { View, Text, ActivityIndicator } from "react-native";
import { Button } from "react-native-elements";
import { MapView } from "expo";
import { connect } from "react-redux";
import * as actions from "../actions";
class MapScreen extends Component {
state = {
region: {
longitude: 30.2672,
latitude: 97.7431,
longitudeDelta: 0.04,
latitudeDelta: 0.09
}
};
onButtonPress = () => {
this.props.fetchJobs(this.state.region, () => {
this.props.navigation.navigate("deck");
});
};
getLocationHandler = () => {
navigator.geolocation.getCurrentPosition(pos => {
const currentCoords = {
longitude: pos.coords.longitude,
latitude: pos.coords.latitude
};
this.goToLocation(currentCoords);
});
};
goToLocation = coords => {
this.map.animateToRegion({
...this.state.region,
longitude: coords.longitude,
latitude: coords.latitude
});
this.setState(prevState => {
return {
region: {
...prevState.region,
longitude: coords.longitude,
latitude: coords.latitude
}
};
});
};
render() {
return (
<View style={{ flex: 1 }}>
<MapView
initialRegion={this.state.region}
style={{ flex: 1 }}
ref={ref => (this.map = ref)}
/>
<View style={styles.buttonContainer}>
<Button
title="Search This Area"
icon={{ name: "search" }}
onPress={this.onButtonPress}
/>
</View>
<View>
<Button
title="My Location"
icon={{ name: "map" }}
onPress={this.getLocationHandler}
/>
</View>
</View>
);
}
}
const styles = {
buttonContainer: {
position: "absolute",
bottom: 50,
left: 0,
right: 0
}
};
export default connect(
null,
actions
)(MapScreen);
This should be an array of objects as verified here:
And in my reducer I have:
import { FETCH_JOBS } from "../actions/types";
const INITIAL_STATE = {
listing: []
};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
return action.payload;
default:
return state;
}
}
I added some verbose error handling and this is what I got back:
[02:25:28] fetchJobs Action Error: Given action "fetch_jobs", reducer
"jobs" returned undefined. To ignore an action, you must explicitly
return the previous state. If you want this reducer to hold no value,
you can return null instead of undefined.
So it seems like the problem is in the jobs_reducer:
import { FETCH_JOBS } from "../actions/types";
const INITIAL_STATE = {
listing: []
};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
return action.payload;
default:
return state;
}
}
I don't know if I am just too exhausted at this point, but I have tried listings: [], I have tried listing: [], I am out of ideas of how to get this reducer to not return undefined because even when I do this:
import { FETCH_JOBS } from "../actions/types";
// const INITIAL_STATE = {
// listing: []
// };
export default function(state = null, action) {
switch (action.type) {
case FETCH_JOBS:
return action.payload;
default:
return state;
}
}
I get the same error message.
My idea with creating an INITIAL_STATE and setting it to listing: [] is to ensure I could map over this array and never worry about the case where I have not yet fetched the list of jobs.
So I am perplexed as to exactly where I am getting this undefined since I did set the initial state to null and I was still getting that error.
So in the process of debugging I then tried this:
import { FETCH_JOBS } from "../actions/types";
// const INITIAL_STATE = {
// listing: []
// };
export default function(state = null, action) {
console.log("action is", action);
switch (action.type) {
case FETCH_JOBS:
return action.payload;
default:
return state;
}
}
And got that the payload is undefined:
Please check your inputs.
[09:39:38] action is Object {
[09:39:38] "payload": undefined,
[09:39:38] "type": "fetch_jobs",
[09:39:38] }
I have hit a wall here. I did a whole refactor to my jobs action creator and logged out the payload property:
export const fetchJobs = (region, distance = 10) => async dispatch => {
try {
const url = buildJobsUrl();
let job_list = await axios.get(url);
job_list = locationify(
region,
console.log(job_list.data.listings.listing),
job_list.data.listings.listing,
distance,
(obj, coords) => {
obj.company.location = { ...obj.company.location, coords };
return obj;
}
);
dispatch({ type: FETCH_JOBS, payload: job_list });
} catch (e) {
console.log("fetchJobs Action Error:", e.message);
}
};
The console.log(job_list.data.listings.listing) logged out the data to my terminal successfully and yet my payload property is still undefined, how is that possible?
I got the action creator and reducer working by refactoring the action creator to just this:
import axios from "axios";
import { Location } from "expo";
import qs from "qs";
import { FETCH_JOBS } from "./types";
// import locationify from "../tools/locationify";
const JOB_ROOT_URL = "https://authenticjobs.com/api/?";
const JOB_QUERY_PARAMS = {
api_key: "<api_key>",
method: "aj.jobs.search",
perpage: "10",
format: "json",
keywords: "javascript"
};
const buildJobsUrl = zip => {
const query = qs.stringify({ ...JOB_QUERY_PARAMS });
return `${JOB_ROOT_URL}${query}`;
};
export const fetchJobs = (region, callback) => async dispatch => {
try {
let zip = await Location.reverseGeocodeAsync(region);
const url = buildJobsUrl(zip);
console.log(url);
let { data } = await axios.get(url);
dispatch({ type: FETCH_JOBS, payload: data });
callback();
} catch (e) {
console.error(e);
}
};
So the problem is no longer there in theory, right. Then, when I bring in the Swipe.js component, the problem returns, in particular the problem seems to be with this code here:
renderCards() {
if (this.state.index >= this.props.data.length) {
return this.props.renderNoMoreCards();
}
return this.props.data
.map((item, i) => {
if (i < this.state.index) {
return null;
}
if (i === this.state.index) {
return (
<Animated.View
key={item[this.props.id]}
style={[this.getCardStyle(), styles.cardStyle]}
{...this.state.panResponder.panHandlers}
>
{this.props.renderCard(item)}
</Animated.View>
);
}
return (
<Animated.View
key={item[this.props.id]}
style={[styles.cardStyle, { top: 10 * (i - this.state.index) }]}
>
{this.props.renderCard(item)}
</Animated.View>
);
})
.reverse();
}
This is where I start to hit a roadblock again.
Props are not available immediatelly from redux store on render, it comes asyncrounously.
To select data from redux store better to use save navigation:
const mapStateToProps = state => ({
jobs: state && state.jobs && state.jobs.listing
})
Than again on render to check if data exists or not:
...
render() {
const { jobs } = this.props;
return (
<View>
{jobs && <Swipe data={jobs} renderCard={this.renderCard} />}
</View>
}
...
renderCards() {
const { data } = this.props;
return data && data.map((item, index) => {
...
map function generally iterate through array-object. You are trying to iterate through a non-array object. So first check the type of the object using typeof(variable) then use the function.
Looks like what helped was refactoring my jobs_reducer file from:
import { FETCH_JOBS } from "../actions/types";
const INITIAL_STATE = {
listing: []
};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
return action.payload;
default:
return state;
}
}
to this:
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
const { listings } = action.payload;
return { ...state, listing: listings.listing };
default:
return state;
}
}

Categories