React Native Ignite错误:在传奇的takeLatest中未被捕获

时间:2018-04-01 15:20:58

标签: javascript reactjs react-native redux react-redux

我是新的反应原生与点燃样板。我尝试使用API​​来创建用于登录检查用户数据的简单功能。但是,我收到错误:在登录时未捕获的最新内容ReferenceError:未定义调用。

这是我的代码: Api.js

// a library to wrap and simplify api calls
import apisauce from 'apisauce'

// our "constructor"
const create = (baseURL = 'http://localhost:4200/') => {
  // ------
  // STEP 1
  // ------
  //
  // Create and configure an apisauce-based api object.
  //
  const api = apisauce.create({
    // base URL is read from the "constructor"
    baseURL,
    // here are some default headers
    headers: {
      'Cache-Control': 'no-cache'
    },
    // 10 second timeout...
    timeout: 10000
  })

  // ------
  // STEP 2
  // ------
  //
  // Define some functions that call the api.  The goal is to provide
  // a thin wrapper of the api layer providing nicer feeling functions
  // rather than "get", "post" and friends.
  //
  // I generally don't like wrapping the output at this level because
  // sometimes specific actions need to be take on `403` or `401`, etc.
  //
  // Since we can't hide from that, we embrace it by getting out of the
  // way at this level.
  //
  const getRoot = () => api.get('')
  const getRate = () => api.get('rate_limit')
  const getUser = (username) => api.get('search/users', {q: username})

  //Method for Backend-Server

  //Users Methods
  const getUsers = () => api.get('/users')
  const login = (username, password) => api.post('users/login', {username: username, password: password})
  // ------
  // STEP 3
  // ------
  //
  // Return back a collection of functions that we would consider our
  // interface.  Most of the time it'll be just the list of all the
  // methods in step 2.
  //
  // Notice we're not returning back the `api` created in step 1?  That's
  // because it is scoped privately.  This is one way to create truly
  // private scoped goodies in JavaScript.
  //
  return {
    // a list of the API functions from step 2
    getRoot,
    getRate,
    getUser,
    getUsers,
    login
  }
}

// let's return back our create method as the default.
export default {
  create
}

LoginRedux.js:

import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  loginRequest: ['username', 'password'],
  loginSuccess: ['username'],
  loginFailure: ['error'],
  logout: null
})

export const LoginTypes = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  username: null,
  error: null,
  payload: null,
  fetching: false
})

/* ------------- Reducers ------------- */

// we're attempting to login
export const request = (state) => state.merge({ fetching: true, payload: null })

// we've successfully logged in
export const success = (state, { username }) =>
  state.merge({ fetching: false, error: null, username })

// we've had a problem logging in
export const failure = (state, { error }) =>
  state.merge({ fetching: false, error })

// we've logged out
export const logout = (state) => INITIAL_STATE

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.LOGIN_REQUEST]: request,
  [Types.LOGIN_SUCCESS]: success,
  [Types.LOGIN_FAILURE]: failure,
  [Types.LOGOUT]: logout
})

/* ------------- Selectors ------------- */

// Is the current user logged in?
export const isLoggedIn = (loginState) => loginState.username !== null

Redux index.js:

import { combineReducers } from 'redux'
import configureStore from './CreateStore'
import rootSaga from '../Sagas/'

export default () => {
  /* ------------- Assemble The Reducers ------------- */
  const rootReducer = combineReducers({
    nav: require('./NavigationRedux').reducer,
    github: require('./GithubRedux').reducer,
    login: require('./LoginRedux').reducer,
    search: require('./SearchRedux').reducer,
    users: require('./UsersRedux').reducer
  })

  return configureStore(rootReducer, rootSaga)
}

LoginSagas.js:

import { put } from 'redux-saga/effects'
import LoginActions from '../Redux/LoginRedux'

// attempts to login
export function * login (api, { username, password }) {
  const response = yield call(api.login, username, password)

  if(response.ok) {
     // dispatch successful logins
     yield put(LoginActions.loginSuccess(username))
  } else {
    yield put(LoginActions.loginFailure('WRONG'))
  }
  // if (password === '') {
  //   // dispatch failure
  //   yield put(LoginActions.loginFailure('WRONG'))
  // } else {
  //   // dispatch successful logins
  //   yield put(LoginActions.loginSuccess(username))
  // }
}
佐贺的

index.js:

import { takeLatest, all } from 'redux-saga/effects'
import API from '../Services/Api'
import FixtureAPI from '../Services/FixtureApi'
import DebugConfig from '../Config/DebugConfig'

/* ------------- Types ------------- */

import { StartupTypes } from '../Redux/StartupRedux'
import { GithubTypes } from '../Redux/GithubRedux'
import { LoginTypes } from '../Redux/LoginRedux'
import { UsersTypes } from '../Redux/UsersRedux'

/* ------------- Sagas ------------- */

import { startup } from './StartupSagas'
import { login } from './LoginSagas'
import { getUserAvatar } from './GithubSagas'
import { getUsers } from './UsersSagas'

/* ------------- API ------------- */

// The API we use is only used from Sagas, so we create it here and pass along
// to the sagas which need it.
const api = DebugConfig.useFixtures ? FixtureAPI : API.create()

/* ------------- Connect Types To Sagas ------------- */

export default function * root () {
  yield all([
    // some sagas only receive an action
    takeLatest(StartupTypes.STARTUP, startup),
    takeLatest(LoginTypes.LOGIN_REQUEST, login, api),

    // some sagas receive extra parameters in addition to an action
    takeLatest(GithubTypes.USER_REQUEST, getUserAvatar, api),
    takeLatest(UsersTypes.USERS_REQUEST, getUsers, api)
  ])
}

LoginScreen.js

import React, { PropTypes } from "react";
import { View, ScrollView, Text, TextInput, TouchableOpacity, Image, Keyboard, LayoutAnimation } from "react-native";
import { connect } from "react-redux";
import Styles from "./Styles/LoginScreenStyles";
import { Images, Metrics } from "../Themes";
import LoginActions from "../Redux/LoginRedux";
import { Button, Text as NBText, Contant, Form, Item, Input, Label } from "native-base";
import UsersActions from "../Redux/UsersRedux"
class LoginScreen extends React.Component {
    static propTypes = {
        dispatch: PropTypes.func,
        fetching: PropTypes.bool,
        attemptLogin: PropTypes.func,
        getUsers: PropTypes.func
    };

    isAttempting = false;
    keyboardDidShowListener = {};
    keyboardDidHideListener = {};

    constructor(props) {
        super(props);
        this.props.getUsers();
        this.state = {
            username: "reactnative@infinite.red",
            password: "password",
            visibleHeight: Metrics.screenHeight,
            topLogo: { width: Metrics.screenWidth - 40 },
        };
        this.isAttempting = false;
    }

    componentWillReceiveProps(newProps) {
        this.forceUpdate();
        // Did the login attempt complete?
        if (this.isAttempting && !newProps.fetching) {
            this.props.navigation.goBack();
        }
        if(newProps.users!= null) {
            console.log(newProps.users)
        }
        if(newProps.login != null) {
            console.log("LOGIN TRY");
            console.log(newProps.login);
        }
        if(newProps.fetching != null) {
            console.log("LOGIN TRY 2");
            console.log(newProps.fetching);
        }
    }

    componentWillMount() {
        // Using keyboardWillShow/Hide looks 1,000 times better, but doesn't work on Android
        // TODO: Revisit this if Android begins to support - https://github.com/facebook/react-native/issues/3468
        this.keyboardDidShowListener = Keyboard.addListener("keyboardDidShow", this.keyboardDidShow);
        this.keyboardDidHideListener = Keyboard.addListener("keyboardDidHide", this.keyboardDidHide);
    }

    componentWillUnmount() {
        this.keyboardDidShowListener.remove();
        this.keyboardDidHideListener.remove();
    }

    keyboardDidShow = e => {
        // Animation types easeInEaseOut/linear/spring
        LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
        let newSize = Metrics.screenHeight - e.endCoordinates.height;
        this.setState({
            visibleHeight: newSize,
            topLogo: { width: 100, height: 70 },
        });
    };

    keyboardDidHide = e => {
        // Animation types easeInEaseOut/linear/spring
        LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
        this.setState({
            visibleHeight: Metrics.screenHeight,
            topLogo: { width: Metrics.screenWidth - 40 },
        });
    };

    handlePressLogin = () => {
        // const { username, password } = this.state
        // this.isAttempting = true
        // attempt a login - a saga is listening to pick it up from here.
        this.props.attemptLogin(this.state.username, this.state.password);
        console.log("Try to login");
        console.log(this.props.login);
        this.props.navigation.navigate("LaunchScreen");
    };

    handleChangeUsername = text => {
        this.setState({ username: text });
    };

    handleChangePassword = text => {
        this.setState({ password: text });
    };

    render() {
        const { username, password } = this.state;
        const { fetching } = this.props;
        const editable = !fetching;
        const textInputStyle = editable ? Styles.textInput : Styles.textInputReadonly;
        return (
            <ScrollView
                contentContainerStyle={{ justifyContent: "center" }}
                style={[Styles.container, { height: this.state.visibleHeight }]}
                keyboardShouldPersistTaps="always"
            >
                <Image source={Images.logo} style={[Styles.topLogo, this.state.topLogo]} />
                <View style={Styles.form}>
                    <Form>
                        <Item stackedLabel>
                            <Label>Username</Label>
                            <Input
                                ref="username"
                                value={username}
                                editable={editable}
                                keyboardType="default"
                                returnKeyType="next"
                                autoCapitalize="none"
                                autoCorrect={false}
                                onChangeText={this.handleChangeUsername}
                                underlineColorAndroid="transparent"
                                onSubmitEditing={() => this.password._root.focus()}
                            />
                        </Item>
                        <Item stackedLabel>
                            <Label>Password</Label>
                            <Input
                                ref={ref => (this.password = ref)}
                                value={password}
                                editable={editable}
                                keyboardType="default"
                                returnKeyType="go"
                                autoCapitalize="none"
                                autoCorrect={false}
                                secureTextEntry
                                onChangeText={this.handleChangePassword}
                                underlineColorAndroid="transparent"
                                onSubmitEditing={this.handlePressLogin}
                            />
                        </Item>
                    </Form>
                    <View style={[Styles.loginRow]}>
                        <Button style={{ flex: 1, justifyContent: "center" }} full onPress={this.handlePressLogin}>
                            <NBText>Sign In</NBText>
                        </Button>
                        <Button
                            style={{ flex: 1, justifyContent: "center" }}
                            full
                            onPress={() => this.props.navigation.goBack()}
                        >
                            <NBText>Cancel</NBText>
                        </Button>
                    </View>
                </View>
            </ScrollView>
        );
    }
}

const mapStateToProps = state => {
    return {
        fetching: state.login.fetching,
        login: state.login,
        users: state.users
    };
};

const mapDispatchToProps = dispatch => {
    return {
        attemptLogin: (username, password) => dispatch(LoginActions.loginRequest(username, password)),
        getUsers: () => dispatch(UsersActions.usersRequest())
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);

请帮助谢谢。

1 个答案:

答案 0 :(得分:0)

您需要import call才能使用

import {call, put} from 'redux-saga/effects',在 LoginSagas.js