作为道具传递的函数不是函数
Posted
技术标签:
【中文标题】作为道具传递的函数不是函数【英文标题】:Function passed as props is not a function 【发布时间】:2019-12-01 13:29:54 【问题描述】:我正在尝试创建一个登录模块。我有一个 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+)*(\.\w2,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
不是一个组件,或者至少它不像一个组件那样使用。
我使用组件是因为我想将参数作为构造函数注入发送。基本上这个想法是 LoginView 将调用 LoginController 来处理它的用户交互,任何与 UI 相关的更改都将在 LoginView 类中完成。这样,逻辑就分开了。但是,如果有其他逻辑可以用来实现相同的理想,请告诉我
【参考方案1】:
这里的问题是LoginController
不是Component
,如果您希望LoginController
只是一个辅助类,那么您应该从中删除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+)*(\.\w2,3)+$/;
var isValid = reg.test(this.email);
if (isValid)
isValid = (this.password.trim().length > 0);
console.log(" Tis is Valid " + isValid);
return isValid
但是,如果您的目标是抽象有状态逻辑,那么您做错了。当你从一个类扩展 React.Component
时,你明确告诉 React 这个类是一个 Component
因此它应该返回 JSX
(render()) 并且应该初始化为一个组件:<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
完成。
【讨论】:
在这种情况下,构造函数会报错。如何将函数作为参数从 LoginView 传递给 LoginController? 给出一个错误,因为您尝试在非组件内部使用状态和道具【参考方案2】:您没有将函数作为道具传递给 LoginController 组件。
【讨论】:
但我写的是 const con = new LoginController(this.changeLoadingState);【参考方案3】:感谢 Dupocas 的回复,我明白何时使用组件,何时不使用。
在我的例子中,LoginController 不应该是一个组件,因为它的逻辑中没有什么可以渲染的。它纯粹是一个辅助类。
现在结果代码如下
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+)*(\.\w2,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,但我相信,如果我不需要组件,我应该以非组件方式尝试它,尽管它很有见地并且可能会在未来复杂的情况下帮助我场景。
【讨论】:
以上是关于作为道具传递的函数不是函数的主要内容,如果未能解决你的问题,请参考以下文章