我决定重用我认为适用于引入第三方API的新应用程序的组件。
有问题的可重用组件正在迭代the_var
,该this.props.data.map()
在我的components/Swipe.js
文件中被评估为未定义:
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;
我不清楚为什么会这样,因为我确实在动作创建者中得到了一个payload: data
:
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);
}
};
那为什么data
在我的可重用组件中被评估为未定义?
在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);
在MapScreen
屏幕上,我所按下的使我出现此错误的按钮:
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);
在我的减速器中,我有:
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;
}
}
我添加了一些详细的错误处理,这是我得到的:
[02:25:28] fetchJobs操作错误:给定操作“ fetch_jobs”,减速器 “工作”返回未定义。要忽略动作,必须明确 返回先前的状态。如果您希望该减速器不具有任何价值, 您可以返回null而不是未定义。
所以看来问题出在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;
}
}
我不知道我现在是否精疲力尽,但是我尝试过listings: []
,我已经尝试过listing: []
,但是我对如何减少这种减速器的想法一无所知。返回undefined
,因为即使我这样做:
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;
}
}
我收到相同的错误消息。
创建INITIAL_STATE
并将其设置为listing: []
的想法是确保我可以映射到该数组,而不必担心我还没有获取作业列表的情况。
由于我确实将初始状态设置为null,但仍然遇到该错误,所以我困惑于确切的位置,因为我确实将初始状态设置为null。
因此,在调试过程中,我尝试了以下方法:
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;
}
}
发现payload
是不确定的:
Please check your inputs.
[09:39:38] action is Object {
[09:39:38] "payload": undefined,
[09:39:38] "type": "fetch_jobs",
[09:39:38] }
我在这里撞墙了。我对jobs
动作创建者进行了完整的重构,并注销了payload
属性:
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);
}
};
console.log(job_list.data.listings.listing)
已成功将数据注销到我的终端,但是我的payload
属性仍未定义,这怎么可能?
通过将动作创建者重构为这样,我使动作创建者和减速器工作:
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);
}
};
所以问题在理论上不再存在,对。然后,当我引入Swipe.js
组件时,问题又回来了,特别是问题似乎出在这里的这段代码上:
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();
}
这是我再次遇到障碍的地方。
答案 0 :(得分:0)
道具不能立即从渲染器上的redux存储中获得,它是异步产生的。 要更好地从redux存储中选择数据,请使用保存导航:
const mapStateToProps = state => ({
jobs: state && state.jobs && state.jobs.listing
})
再次在渲染中检查数据是否存在:
...
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) => {
...
答案 1 :(得分:0)
map 函数通常会遍历数组对象。您正在尝试遍历非数组对象。因此,首先使用<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<nav class="navbar navbar-expand-md bg-dark navbar-dark bg-custom sticky-top">
<div class="container">
<a class="navbar-brand" href="#">
<i class='far fa-image' style='font-size:24px'></i> IMGS</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Contact</a>
</li>
</ul>
<ul class="navbar-nav mr-0">
<li class="nav-item">
<a class="nav-link" href="#">Sign Up</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Login</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container my-3">
<div class="jumbotron py-4 mb-3">
<h1>
<i class='fas fa-camera-retro' style='font-size:40px'></i> The Image Gallery</h1>
<p>A bunch of beautiful images that I didn't take(except the first one!)</p>
</div>
<div class="row ">
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1481889617387-82a8f2413b6b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1430026996702-608b84ce9281?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1551356279-8337da8863d3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1551897628-d053c089e823?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1555939765-9b78f509d500?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1516911588919-bf6b970eaf32?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1551222998-8a604fda1d22?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1550977088-b31835bf616d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
<div class="col-sm-6 col-md-4 my-2">
<img src="https://images.unsplash.com/photo-1551290465-0ac783e20542?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" alt="" class="img-thumbnail mx-auto d-block img-fluid">
</div>
</div>
</div>
检查对象的类型,然后使用函数。
答案 2 :(得分:0)
看起来像是从以下位置重构了我的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;
}
}
对此:
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;
}
}