您好我仍然是本土的反应,我很困惑如何使用react redux特别是对于点燃模板。我按照本教程https://medium.com/skyshidigital/interaksi-api-dengan-ignite-react-native-f83bf35a23f5进行操作。这是我最新的错误"未检查"," take(patternOrChannel):patternOrChannel未定义"。请帮忙告诉我代码的哪一部分给我一个错误。
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})
const getUsers = () => api.get('/users')
// ------
// 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
}
}
// let's return back our create method as the default.
export default {
create
}
UsersRedux.js
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
usersRequest: null,
usersSuccess: ['data'],
usersFailure: null
})
export const UsersTypes = Types
export default Creators
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
data: null,
fetching: null,
payload: null,
error: null
})
/* ------------- Reducers ------------- */
// request the data from an api
export const request = (state, { data }) =>
state.merge({ fetching: true, payload: null })
// successful api lookup
export const success = (state, action) => {
const { payload } = action
return state.merge({ fetching: false, error: null, data })
}
// Something went wrong somewhere.
export const failure = state =>
state.merge({ fetching: false, error })
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.USERS_REQUEST]: request,
[Types.USERS_SUCCESS]: success,
[Types.USERS_FAILURE]: failure
})
来自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)
}
UsersSagas.js:
/* ***********************************************************
* A short word on how to use this automagically generated file.
* We're often asked in the ignite gitter channel how to connect
* to a to a third party api, so we thought we'd demonstrate - but
* you should know you can use sagas for other flow control too.
*
* Other points:
* - You'll need to add this saga to sagas/index.js
* - This template uses the api declared in sagas/index.js, so
* you'll need to define a constant in that file.
*************************************************************/
import { call, put } from 'redux-saga/effects'
import UsersActions from '../Redux/UsersRedux'
export function * getUsers (api, action) {
const { data } = action
// make the call to the api
const response = yield call(api.getUsers, data)
// success?
if (response.ok) {
// You might need to change the response here - do this with a 'transform',
// located in ../Transforms/. Otherwise, just pass the data back from the api.
yield put(UsersActions.usersSuccess(response.data))
} else {
yield put(UsersActions.usersFailure())
}
}
来自Sagas的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),
// some sagas receive extra parameters in addition to an action
takeLatest(GithubTypes.USER_REQUEST, getUserAvatar, api),
takeLatest(UsersTypes.USER_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, StyleProvider } from "native-base";
import buttonTheme from '../../native-base-theme/components/';
import commonColor from '../../native-base-theme/variables/commonColor';
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: "",
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();
}
}
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 - 100,
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(username, password);
this.props.navigation.navigate("LaunchScreen");
};
handlePressRegister = () => {
// const { username, password } = this.state
// this.isAttempting = true
// attempt a login - a saga is listening to pick it up from here.
// this.props.attemptLogin(username, password);
this.props.navigation.navigate("RegisterScreen");
};
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={require("../Images/tuku_omah_logo_login.jpeg")} 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]}>
<StyleProvider style={buttonTheme(commonColor)}>
<Button style={{ flex: 1, justifyContent: "center" }} full onPress={this.handlePressLogin}>
<NBText>Sign In</NBText>
</Button>
</StyleProvider>
</View>
<Text style={Styles.registerText} onPress={this.handlePressRegister}>
Tidak punya akun? Daftar di sini
</Text>
</View>
</ScrollView>
);
}
}
const mapStateToProps = state => {
return {
//fetching: state.login.fetching,
users: state.users
};
};
const mapDispatchToProps = dispatch => {
return {
//attemptLogin: (username, password) => dispatch(LoginActions.loginRequest(username, password)),
getUsers: () => dispatch(UsersActions => dispatch(UsersActions.getUsers()))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
答案 0 :(得分:1)
在usersRedux.js中,您可以定义UsersTypes。
其中一个是UsersTypes.USERS_REQUEST。
在sagas的index.js中,您尝试在UsersTypes.USER_REQUEST上运行您的用户传奇。
此操作类型值不存在,您在USERS_REQUEST中缺少S。
你传递给takeLatest以开始你的传奇的actionType在这种情况下被称为模式。