作为 props 传递的函数不是函数
Function passed as props is not a function
我正在尝试创建一个登录模块。我有一个 LoginView,它定义了视图和一个 LoginController,我在其中定义了所有用户交互。现在我正在尝试合并一个逻辑,其中 LoginController 将更改 LoginView 的状态,如在所有输入数据有效的情况下将 isLoading 的值从 false 更改为 true
登录查看
import React, { Component, Fragment} from 'react';
import LoginController from '../Controller/LoginController.js';
import {
View,
ScrollView,
StatusBar,
SafeAreaView,
TextInput,
TouchableOpacity,
Text,
StyleSheet
} from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 23
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText:{
color: 'white'
}
});
export default class LoginView extends Component {
constructor(){
super()
this.state = {
isLoading: false
}
}
changeLoadingState = (currentLoadingState) => {
/* Create a loader screen and incorporate it here.
*/
this.setState({isLoading: currentLoadingState} , () => {
console.log("This is called when this.setState has resolved");
console.log(this.state.isLoading);
});
}
render() {
const con = new LoginController(this.changeLoadingState);
return (
<Fragment>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style = {styles.container}>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Email"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handleEmail}/>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Password"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handlePassword}/>
<TouchableOpacity
style = {styles.submitButton}
onPress = {
() => con.login()
}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</Fragment>
);
}
}
LoginController.js
import React, { Component } from 'react';
import LoginNetworkManager from '../NetworkManager/LoginNetworkManager.js';
import Loader from '../../Utils/Loader.js';
export default class LoginController extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null
};
this.changeLoadingState = this.changeLoadingState.bind(this);
}
changeLoadingState = (currentLoadingState) => {
this.props.changeLoadingState(currentLoadingState);
}
handleEmail = (text) => {
this.setState({email: text});
}
handlePassword = (text) => {
this.setState({password: text});
}
login = () => {
this.changeLoadingState(this.validate());
if (this.validate() == true) {
// Here in we will call the API
} else {
console.log(" It's false ");
// Do nothing
}
}
validate = () => {
var reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
var isValid = reg.test(this.email);
if (isValid) {
isValid = (this.password.trim().length > 0);
}
console.log(" Tis is Valid " + isValid);
return isValid
}
}
点击登录按钮时的错误是
_this.props.changeLoadingState is not a function
handleException @ ExceptionsManager.js:86
handleError @ setUpErrorHandling.js:23
reportFatalError @ error-guard.js:42
__guard @ MessageQueue.js:345
callFunctionReturnFlushedQueue @ MessageQueue.js:105
(anonymous) @ debuggerWorker.js:80
您没有将该函数作为道具传递给您的 LoginController 组件。
这里的问题是 LoginController
不是 Component
,如果你想让 LoginController
只是一个助手 class,你应该删除 state
和 props
来自它:
export default class LoginController {
changeLoadingState = (currentLoadingState) => {
}
handleEmail = (text) => {
}
handlePassword = (text) => {
}
login = () => {
this.changeLoadingState(this.validate());
if (this.validate() == true) {
// Here in we will call the API
} else {
console.log(" It's false ");
// Do nothing
}
}
validate = () => {
var reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
var isValid = reg.test(this.email);
if (isValid) {
isValid = (this.password.trim().length > 0);
}
console.log(" Tis is Valid " + isValid);
return isValid
}
}
但是,如果您的目标是抽象有状态逻辑,那么您就错了。当您从 class 扩展 React.Component
时,您明确告诉 React 这个 class 是一个 Component
因此它应该 return JSX
(渲染( )) 并应初始化为组件:<LoginController />
,要抽象有状态逻辑,您实际上有很多非常酷的选择:
高阶组件 (HOC)
这似乎是您的用例,因为您想将一些道具注入 LoginView
,因此您可以将逻辑抽象为 HOC:
import React, { Component } from 'react';
import LoginNetworkManager from '../NetworkManager/LoginNetworkManager.js';
import Loader from '../../Utils/Loader.js';
export default withLogin = (ChildComponent) => {
return class LoginController extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null
};
this.changeLoadingState = this.changeLoadingState.bind(this);
}
/*
Your logic
*/
render(){
return <ChildComponent {...this.state} />
}
}
}
现在在 LoginView
中,您可以这样导出:export default withLogin(LoginView)
和 LoginController
的状态将在 LoginView
的属性中序列化:this.props.email
和this.props.password
当然,使用 HOC 可以完成的所有事情也可以使用 renderProps
和 hooks
来完成。
感谢 Dupocas 的回复,我明白了何时使用组件,何时不使用。
在我的例子中,LoginController 不应该是一个组件,因为在它的逻辑中没有什么可以渲染的。纯属帮手class.
现结果代码如下
import React, { Component, Fragment} from 'react';
import LoginController from '../Controller/LoginController.js';
import Loader from '../../Utils/Loader';
import {
View,
StatusBar,
SafeAreaView,
TextInput,
TouchableOpacity,
Text,
StyleSheet
} from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 23
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText:{
color: 'white'
}
});
export default class LoginView extends Component {
constructor(){
super()
this.state = {
isLoading: false
}
con = new LoginController();
}
changeLoadingState = (currentLoadingState,completionBlock) => {
this.setState({isLoading: currentLoadingState} , completionBlock);
}
render() {
return (
<Fragment>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style = {styles.container}>
<Loader
loading={this.state.isLoading} />
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Email"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handleEmail}/>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Password"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handlePassword}/>
<TouchableOpacity
style = {styles.submitButton}
onPress = {
() => con.login(this.changeLoadingState)
}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</Fragment>
);
}
}
登录控制器是
import LoginNetworkManager from '../NetworkManager/LoginNetworkManager.js';
export default class LoginController {
email = null;
password = null;
changeLoadingState = (currentLoadingState,viewCallback,completionBlock) => {
viewCallback(currentLoadingState,completionBlock);
}
handleEmail = (text) => {
this.email = text
}
handlePassword = (text) => {
this.password = text
}
login = (viewCallback) => {
this.changeLoadingState(this.validate(),viewCallback);
if (this.validate() == true) {
let params = { email : this.email, password : this.password};
LoginNetworkManager.loginAPI(params, (response,error) => {
this.changeLoadingState(false,viewCallback,() => {
if (error){
}else{
}
});
});
}
}
validate = () => {
var reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
var isValid = reg.test(this.email);
console.log(" this.email " + this.email);
console.log(" this.password " + this.password);
if (isValid) {
isValid = (this.password.trim().length > 0);
console.log(" password validation ----> " + isValid);
}
return isValid
}
}
虽然 Dupocas 提到了 HOC 和 RenderProps 和 Hooks,但我相信,如果我不需要组件,我应该以 non-component 的方式尝试它,尽管它很有见地并且可能会有所帮助我在未来的复杂场景中。
我正在尝试创建一个登录模块。我有一个 LoginView,它定义了视图和一个 LoginController,我在其中定义了所有用户交互。现在我正在尝试合并一个逻辑,其中 LoginController 将更改 LoginView 的状态,如在所有输入数据有效的情况下将 isLoading 的值从 false 更改为 true
登录查看
import React, { Component, Fragment} from 'react';
import LoginController from '../Controller/LoginController.js';
import {
View,
ScrollView,
StatusBar,
SafeAreaView,
TextInput,
TouchableOpacity,
Text,
StyleSheet
} from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 23
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText:{
color: 'white'
}
});
export default class LoginView extends Component {
constructor(){
super()
this.state = {
isLoading: false
}
}
changeLoadingState = (currentLoadingState) => {
/* Create a loader screen and incorporate it here.
*/
this.setState({isLoading: currentLoadingState} , () => {
console.log("This is called when this.setState has resolved");
console.log(this.state.isLoading);
});
}
render() {
const con = new LoginController(this.changeLoadingState);
return (
<Fragment>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style = {styles.container}>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Email"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handleEmail}/>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Password"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handlePassword}/>
<TouchableOpacity
style = {styles.submitButton}
onPress = {
() => con.login()
}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</Fragment>
);
}
}
LoginController.js
import React, { Component } from 'react';
import LoginNetworkManager from '../NetworkManager/LoginNetworkManager.js';
import Loader from '../../Utils/Loader.js';
export default class LoginController extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null
};
this.changeLoadingState = this.changeLoadingState.bind(this);
}
changeLoadingState = (currentLoadingState) => {
this.props.changeLoadingState(currentLoadingState);
}
handleEmail = (text) => {
this.setState({email: text});
}
handlePassword = (text) => {
this.setState({password: text});
}
login = () => {
this.changeLoadingState(this.validate());
if (this.validate() == true) {
// Here in we will call the API
} else {
console.log(" It's false ");
// Do nothing
}
}
validate = () => {
var reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
var isValid = reg.test(this.email);
if (isValid) {
isValid = (this.password.trim().length > 0);
}
console.log(" Tis is Valid " + isValid);
return isValid
}
}
点击登录按钮时的错误是
_this.props.changeLoadingState is not a function
handleException @ ExceptionsManager.js:86
handleError @ setUpErrorHandling.js:23
reportFatalError @ error-guard.js:42
__guard @ MessageQueue.js:345
callFunctionReturnFlushedQueue @ MessageQueue.js:105
(anonymous) @ debuggerWorker.js:80
您没有将该函数作为道具传递给您的 LoginController 组件。
这里的问题是 LoginController
不是 Component
,如果你想让 LoginController
只是一个助手 class,你应该删除 state
和 props
来自它:
export default class LoginController {
changeLoadingState = (currentLoadingState) => {
}
handleEmail = (text) => {
}
handlePassword = (text) => {
}
login = () => {
this.changeLoadingState(this.validate());
if (this.validate() == true) {
// Here in we will call the API
} else {
console.log(" It's false ");
// Do nothing
}
}
validate = () => {
var reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
var isValid = reg.test(this.email);
if (isValid) {
isValid = (this.password.trim().length > 0);
}
console.log(" Tis is Valid " + isValid);
return isValid
}
}
但是,如果您的目标是抽象有状态逻辑,那么您就错了。当您从 class 扩展 React.Component
时,您明确告诉 React 这个 class 是一个 Component
因此它应该 return JSX
(渲染( )) 并应初始化为组件:<LoginController />
,要抽象有状态逻辑,您实际上有很多非常酷的选择:
高阶组件 (HOC)
这似乎是您的用例,因为您想将一些道具注入 LoginView
,因此您可以将逻辑抽象为 HOC:
import React, { Component } from 'react';
import LoginNetworkManager from '../NetworkManager/LoginNetworkManager.js';
import Loader from '../../Utils/Loader.js';
export default withLogin = (ChildComponent) => {
return class LoginController extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null
};
this.changeLoadingState = this.changeLoadingState.bind(this);
}
/*
Your logic
*/
render(){
return <ChildComponent {...this.state} />
}
}
}
现在在 LoginView
中,您可以这样导出:export default withLogin(LoginView)
和 LoginController
的状态将在 LoginView
的属性中序列化:this.props.email
和this.props.password
当然,使用 HOC 可以完成的所有事情也可以使用 renderProps
和 hooks
来完成。
感谢 Dupocas 的回复,我明白了何时使用组件,何时不使用。
在我的例子中,LoginController 不应该是一个组件,因为在它的逻辑中没有什么可以渲染的。纯属帮手class.
现结果代码如下
import React, { Component, Fragment} from 'react';
import LoginController from '../Controller/LoginController.js';
import Loader from '../../Utils/Loader';
import {
View,
StatusBar,
SafeAreaView,
TextInput,
TouchableOpacity,
Text,
StyleSheet
} from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 23
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText:{
color: 'white'
}
});
export default class LoginView extends Component {
constructor(){
super()
this.state = {
isLoading: false
}
con = new LoginController();
}
changeLoadingState = (currentLoadingState,completionBlock) => {
this.setState({isLoading: currentLoadingState} , completionBlock);
}
render() {
return (
<Fragment>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style = {styles.container}>
<Loader
loading={this.state.isLoading} />
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Email"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handleEmail}/>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Password"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {con.handlePassword}/>
<TouchableOpacity
style = {styles.submitButton}
onPress = {
() => con.login(this.changeLoadingState)
}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</Fragment>
);
}
}
登录控制器是
import LoginNetworkManager from '../NetworkManager/LoginNetworkManager.js';
export default class LoginController {
email = null;
password = null;
changeLoadingState = (currentLoadingState,viewCallback,completionBlock) => {
viewCallback(currentLoadingState,completionBlock);
}
handleEmail = (text) => {
this.email = text
}
handlePassword = (text) => {
this.password = text
}
login = (viewCallback) => {
this.changeLoadingState(this.validate(),viewCallback);
if (this.validate() == true) {
let params = { email : this.email, password : this.password};
LoginNetworkManager.loginAPI(params, (response,error) => {
this.changeLoadingState(false,viewCallback,() => {
if (error){
}else{
}
});
});
}
}
validate = () => {
var reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
var isValid = reg.test(this.email);
console.log(" this.email " + this.email);
console.log(" this.password " + this.password);
if (isValid) {
isValid = (this.password.trim().length > 0);
console.log(" password validation ----> " + isValid);
}
return isValid
}
}
虽然 Dupocas 提到了 HOC 和 RenderProps 和 Hooks,但我相信,如果我不需要组件,我应该以 non-component 的方式尝试它,尽管它很有见地并且可能会有所帮助我在未来的复杂场景中。