问题
As a newbie in JS world i am in a big trouble ... I am using a react hook
import { useKeycloak } from '@react-keycloak/web';
import { useCallback } from 'react';
export const useAuthenticatedCallback = (callbackFn) => {
const [keycloak, initialized] = useKeycloak()
const authCallback = useCallback(() => {
// Do nothing while Keycloak is initializing
if (!initialized) {
return
}
// if user is not authenticated redirect to login
if (!keycloak.authenticated) {
return keycloak.login()
}
// otherwise invoke function
return callbackFn()
}, [callbackFn, initialized, keycloak])
return authCallback
}
and able to use inside a function
import React from "react";
import {Grid, Avatar, Chip } from '@material-ui/core';
import { useAuthenticatedCallback } from '../../shared/keycloak/KeycloakOnDemand'
// Tranding function component
function Tranding(props) {
const yourCallback = () => {
// The code you need to be authenticated to run
return yourCallback;
};
const authenticatedCallback = useAuthenticatedCallback(yourCallback);
return (
<Grid container spacing={3}>
<Grid className="text-center" item sm={12} xs={12} md={2}>
<h4>Trending..</h4>
</Grid>
<Grid item md={10}>
<div className="trending-container">
<button onClick={authenticatedCallback} label="ClickMe"/>
</div>
</Grid>
</Grid>
);
}
// export the component.
export default Tranding;
Upto here everything fine and things are working according to requirement.
Now i got a challenge to use same hook inside a react component class ,but fail to achieve this ..Can anyone help for this issue?
I was trying to use in this class
import React from "react";
import {connect} from 'react-redux';
import PostList from "./PostList";
import Grid from '@material-ui/core/Grid';
import Switch from '@material-ui/core/Switch';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { TextField, ButtonGroup, Button } from '@material-ui/core';
import SaveIcon from '@material-ui/icons/Save';
import TextEditor from './TextEditor';
//import CKTextEditor from '../../shared/CKTextEditor';
import {NotificationContainer, NotificationManager} from 'react-notifications';
import CircularSpinner from '../../shared/CircularSpinner';
import postService from '../../services/PostService';
class Posts extends React.Component {
constructor() {
super()
this.state = {
posts: [],
totalPages: 0,
isLoading: false,
postForm: {
isValidationActive: false,
tradeTypeId: 1,
tradeTypeText: 'Buy',
price: '', priceValid: false,
stopLoss: '', stopLossValid: false,
targetPrice: '', targetPriceValid: false,
targetDate: '', targetDateValid: false,
title: '',
notes: '', notesValid: false
},
analysisForm: {
isValidationActive: false,
title: '', titleValid: false,
notes: '', notesTemp:'', notesValid: false
},
isTradeCallActive: true,
tradeCallFormValid: false,
analysisFormValid: false,
companyCode: '',
companyValid: false,
postTypes: [],
tradeCallTypes: [],
companies: [],
tradeTypeSelected: 'Buy'
};
this.formRef = null;
this.handleChange = this.handleChange.bind(this);
this.onCompanyCodeChange = this.onCompanyCodeChange.bind(this);
this.handleEditorChange = this.handleEditorChange.bind(this);
this.handleTradeCallSubmit = this.handleTradeCallSubmit.bind(this);
this.handleAnalysisSubmit = this.handleAnalysisSubmit.bind(this);
this.loadFormModel = this.loadFormModel.bind(this);
this.clearTradeCallForm = this.clearTradeCallForm.bind(this);
this.clearAnalysisForm = this.clearAnalysisForm.bind(this);
this.loadPosts = this.loadPosts.bind(this);
}
// handle chnage events for all controls.
handleChange(event) {
let _postForm = this.state.postForm;
let _isTradeCallActive = this.state.isTradeCallActive;
let _targetValue = event.target.value;
let _companyValid = this.state.companyValid;
let _companyCode = this.state.companyCode;
if(event.target.name === 'TradeCall' || event.target.name === 'Analysis'){
if(event.target.name === 'Analysis' && event.target.checked) {_isTradeCallActive = false;}
if(event.target.name === 'TradeCall' && event.target.checked) {_isTradeCallActive = true;}
}
if(event.target.name === 'txtCompany') {
_companyCode = _targetValue;
if (_targetValue.length < 3) { _companyValid = false; }
}
if(event.target.name === 'txtTitle'){
let _analysisForm = this.state.analysisForm;
_analysisForm.titleValid = true;
_analysisForm.title = _targetValue;
if (_targetValue.length < 10) { _analysisForm.titleValid = false; }
let _analysisFormValid = false;
if(_analysisForm.titleValid && _analysisForm.notesValid) {
_analysisFormValid = true;
}
this.setState({ ...this.state, analysisForm: _analysisForm, analysisFormValid: _analysisFormValid});
return;
}
if(event.target.name === 'txtPrice'
|| event.target.name === 'txtStoploss'
|| event.target.name === 'txtTarget'
|| event.target.name === 'notesTextArea'
|| event.target.name === 'targetDate'
|| event.target.name === 'selectTradeType'){
if(event.target.name === 'txtPrice') {
_postForm.priceValid = true;
_postForm.price = _targetValue;
if (_targetValue.length < 1 || _targetValue < 1) { _postForm.priceValid = false; }
}
if(event.target.name === 'txtStoploss') {
_postForm.stopLossValid = true;
_postForm.stopLoss = _targetValue;
if (_targetValue.length < 1 || _targetValue < 1) { _postForm.stopLossValid = false; }
}
if(event.target.name === 'txtTarget') {
_postForm.targetPriceValid = true;
_postForm.targetPrice = _targetValue;
if (_targetValue.length < 1 || _targetValue < 1) { _postForm.targetPriceValid = false; }
}
if(event.target.name === 'notesTextArea') {
_postForm.notesValid = true;
_postForm.notes = _targetValue;
if (_targetValue.length < 20) { _postForm.notesValid = false; }
}
if(event.target.name === 'targetDate') {
_postForm.targetDateValid = true;
_postForm.targetDate = _targetValue;
if (_targetValue.length < 8) { _postForm.targetDateValid = false; }
}
}
let _tradeType = this.state.tradeCallTypes.find(x => x.Name === this.state.tradeTypeSelected)
_postForm.tradeTypeId = _tradeType.Id;
_postForm.tradeTypeText = _tradeType.Name;
let _tradeCallFormValid = false;
if(this.state.companyValid && _postForm.priceValid && _postForm.stopLossValid && _postForm.targetPriceValid && _postForm.targetDateValid && _postForm.notesValid) {
_tradeCallFormValid = true;
_postForm.title = `${_postForm.tradeTypeText} ${this.state.companyCode} at price ${_postForm.price} for target ${_postForm.targetPrice} by date ${_postForm.targetDate} with SL ${_postForm.stopLoss}`;
}
this.setState({ ...this.state, postForm: _postForm, tradeCallFormValid: _tradeCallFormValid, isTradeCallActive: _isTradeCallActive, companyValid: _companyValid, companyCode: _companyCode });
};
// handle trade call submit click.
handleTradeCallSubmit(){
if(this.state.tradeCallFormValid){
// To DO
this.setState({ ...this.state, tradeCallFormValid: false});
let _postForm = this.state.postForm;
let _companyCode = this.state.companyCode;
let requestBody = {
eventType:'create-trade-call-post',
callType:_postForm.tradeTypeId,
symbol:_companyCode,
userId: this.props.activeUser.Id,
price:_postForm.price,
stopLoss:_postForm.stopLoss,
targetPrice:_postForm.targetPrice,
targetDate:_postForm.targetDate,
tags: _companyCode,
title:_postForm.title,
notes:_postForm.notes
}
postService.create(requestBody)
.then((result) => {
NotificationManager.success(`Trade call post created successfully...`);
this.loadPosts(1);
this.clearTradeCallForm();
}).catch((error) => {
NotificationManager.error(`Trade call post creation failed..`);
console.log(`Error: ${error}`);
});
} else {
let _postForm = this.state.postForm;
_postForm.isValidationActive = true;
this.setState({ ...this.state, postForm: _postForm});
}
}
// component did mount event called after the virtual DOM loaded.
componentDidMount() {
this.setState({ ...this.state, isLoading: true });
this.loadFormModel();
}
render() {
if (this.state.isLoading) {
return <CircularSpinner />;
}
return (
<div>
<NotificationContainer/>
<Card>
<CardContent>
<form ref={(ref) => this.formRef = ref} noValidate autoComplete="off">
<Grid className="text-center" container spacing={2}>
{
this.state.postTypes.map((postType, index) =>
<Grid key={postType.Id} item sm={6} xs={6} md={3}>
<h5>{postType.Name} <Switch key={postType.Id} checked={(postType.Name === 'TradeCall')?this.state.isTradeCallActive: !this.state.isTradeCallActive} value={postType.Id} onChange={this.handleChange} name={postType.Name} inputProps={(postType.Name === 'TradeCall') ? {'aria-label': 'secondary checkbox' }: { 'aria-label': 'primary checkbox' }} /></h5>
</Grid>
)
}
<Grid item sm={12} xs={12} md={6}>
<Autocomplete
id="companyAutocomplete"
options={this.state.companies}
getOptionLabel={(option) => option.Symbol}
onInputChange={this.onCompanyCodeChange}
fullWidth
renderInput={(params) => <TextField fullWidth id="txtCompany" {...params}
error={((this.state.postForm.isValidationActive || this.state.analysisForm.isValidationActive))
&& !this.state.companyValid} name="txtCompany" size="small" label="Company" onChange={this.handleChange} variant="outlined" placeholder="Company.." />}
/>
</Grid>
</Grid>
<div className={!this.state.isTradeCallActive ? 'hidden' : ''}>
<Grid container spacing={2}>
<Grid item sm={12} xs={12} md={2}>
<ButtonGroup fullWidth aria-label="small button group">
<Button onClick={()=>{this.setState({ ...this.state, tradeTypeSelected: "Sale"})}}
variant={(this.state.tradeTypeSelected === "Buy") ? "outlined" : "contained"}
color={(this.state.tradeTypeSelected === "Buy") ? "default" : "secondary"}> Sale
</Button>
<Button onClick={()=>{this.setState({ ...this.state, tradeTypeSelected: "Buy"})}}
variant={(this.state.tradeTypeSelected === "Buy") ? "contained" : "outlined"}
color={(this.state.tradeTypeSelected === "Buy") ? "secondary" : "default"}> Buy
</Button>
</ButtonGroup>
</Grid>
<Grid item sm={12} xs={12} md={2}>
<TextField fullWidth id="txtPrice" error={this.state.postForm.isValidationActive && !this.state.postForm.priceValid} name="txtPrice" type="number" InputProps={{ inputProps: { min: 0} }} size="small" label="Price" onChange={this.handleChange} variant="outlined" placeholder="Price.." />
</Grid>
<Grid item sm={12} xs={12} md={2}>
<TextField fullWidth id="txtStoploss" error={this.state.postForm.isValidationActive && !this.state.postForm.stopLossValid} name="txtStoploss" type="number" InputProps={{ inputProps: { min: 0} }} size="small" label="Stoploss" onChange={this.handleChange} variant="outlined" placeholder="SL.." />
</Grid>
<Grid item sm={12} xs={12} md={2}>
<TextField fullWidth id="txtTarget" error={this.state.postForm.isValidationActive && !this.state.postForm.targetPriceValid} name="txtTarget" type="number" InputProps={{ inputProps: { min: 0} }} size="small" label="Target price" onChange={this.handleChange} variant="outlined" placeholder="Price.." />
</Grid>
<Grid item sm={12} xs={12} md={4}>
<TextField fullWidth id="targetDate" error={this.state.postForm.isValidationActive && !this.state.postForm.targetDateValid} name="targetDate" onChange={this.handleChange} type="date" size="small" label="Target date" variant="outlined" InputLabelProps={{ shrink: true, }} />
</Grid>
</Grid>
<Grid container spacing={2}>
<Grid item sm={12} xs={12} md={12}>
<TextField id="notesTextArea" error={this.state.postForm.isValidationActive && !this.state.postForm.notesValid} name="notesTextArea" onChange={this.handleChange} size="medium" fullWidth
placeholder="Enter notes here.." variant="outlined" label="Notes"
multiline rows={3} rowsMax={4} />
</Grid>
</Grid>
<Grid justify="center" container spacing={2}>
<Grid item sm={12} xs={12} md={3}>
<Button size="medium" fullWidth id="btnSubmit" startIcon={<SaveIcon />} onClick={this.handleTradeCallSubmit} variant="contained" color="primary"> Save </Button>
</Grid>
</Grid>
</div>
</form>
</CardContent>
</Card>
<Grid container spacing={3}>
<Grid item md={12}>
<PostList totalPages={this.state.totalPages} posts={this.state.posts} loadPosts={this.loadPosts} />
</Grid>
</Grid>
</div>
)
}
}
// Map redux state to props
const mapStateToProps = state => ({
activeUser: state.session.activeUser
});
// export the component.
export default connect(mapStateToProps)(Posts);
So before calling handleTradeCallSubmit()
method i want to check user is login or not if not login redirect to login page otherwise process the method?
回答1:
Although use hooks cannot be used inside a class component, hook components can be used inside a class component.
So one solution is to create a component that uses the hook, and then use the component inside the class instead.
function AuthenticatedCallback(props) {
const authenticatedCallback = useAuthenticatedCallback(props.callbackFn);
return props.children(authenticatedCallback);
}
//from inside the class component.
......
return (
<AuthenticatedCallback>{authenticatedCallback =>
<Grid container spacing={3}>
<Grid className="text-center" item sm={12} xs={12} md={2}>
<h4>Trending..</h4>
</Grid>
<Grid item md={10}>
<div className="trending-container">
<button onClick={authenticatedCallback} label="ClickMe"/>
</div>
</Grid>
</Grid>}
</AuthenticatedCallback>
);
Notice how I'm using the props.children
as a functional callback to pass this down to the component.
回答2:
You can not use a Hook inside a react Class.
Hooks are functions that let you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes. (We don’t recommend rewriting your existing components overnight but you can start using Hooks in the new ones if you’d like.)
- https://reactjs.org/docs/hooks-overview.html#:~:text=Hooks%20are%20functions%20that%20let,if%20you'd%20like.)
来源:https://stackoverflow.com/questions/63525070/how-to-use-react-hook-in-a-react-class