https://codesandbox.io/s/6zrw7r66rr
Я разбудил ваши коды и окно и отредактировал 4 файла. Уверен, он удовлетворяет всем вашим требованиям, указанным выше
VerticalLinearStepper.js: здесь мы храним наше состояние username, password, disabledNext (radioButton)
и метод handleChange
для setState
. Затем мы передали состояние до -> Step1.js
-> AsyncValidationForm.js
.
class VerticalLinearStepper extends React.Component {
state = {
activeStep: 0,
//we set our state in this parent
disabledNext: true,
username: "",
password: ""
};
steps = {
"Select campaign settings": Step1,
"Create an ad group": Step2,
"Create an ad": Step3
};
//setState for disabledNext
handleChangeDisabledNext = value => {
this.setState({ disabledNext: value });
};
//setState for username, password
handleChange = (name, value) => {
this.setState({ [name]: value });
};
stepsCount = () => Object.values(this.steps).length;
canGoBack = () => this.state.activeStep > 0;
canGoForward = () => this.state.activeStep < this.stepsCount();
isFinished = () => this.state.activeStep === this.stepsCount();
handleBack = () => {
if (this.canGoBack()) {
this.setState(prevState => ({ activeStep: prevState.activeStep - 1 }));
}
};
handleNext = () => {
if (this.canGoForward()) {
this.setState(prevState => ({ activeStep: prevState.activeStep + 1 }));
}
};
handleReset = () => this.setState({ activeStep: 0 });
render() {
const { classes } = this.props;
const { activeStep } = this.state;
return (
<div className={classes.root}>
<Stepper activeStep={activeStep} orientation="vertical">
{Object.entries(this.steps).map(([label, CustomStep]) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent>
<CustomStep
canGoBack={this.canGoBack()}
canGoForward={this.canGoForward()}
onBack={this.handleBack}
onNext={this.handleNext}
classes={classes}
//we pass down the state and its' setState method
handleChangeDisabledNext={this.handleChangeDisabledNext}
disabledNext={this.state.disabledNext}
handleChange={this.handleChange}
username={this.state.username}
password={this.state.password}
/>
</StepContent>
</Step>
))}
</Stepper>
{this.isFinished() && (
<Paper square elevation={0} className={classes.resetContainer}>
<Typography>All steps completed - you're finished</Typography>
<Button onClick={this.handleReset} className={classes.button}>
Reset
</Button>
</Paper>
)}
</div>
);
}
}
В AsyncValidationForm.js
мы связываем метод onChange
для отслеживания значения и вызываем метод setState
и this.props.handleChange
для setState
в VerticalLinearStepper.js
const renderField = ({
input,
label,
type,
//checked is for radio, initialValue is for setting the username, password value
checked,
initialValue,
meta: { asyncValidating, touched, error }
}) => {
return (
<div>
<label>{label}</label>
<div className={asyncValidating ? "async-validating" : ""}>
<input
{...input}
value={initialValue} //add value attr
checked={checked} //add checked attr
type={type}
placeholder={label}
/>
{touched && error && <span>{error}</span>}
</div>
</div>
);
};
class AsyncValidationForm extends React.Component {
constructor(props) {
super(props);
console.log("AsyncValidationForm ---->");
this.state = {
//pass down VerticalLinearStepper.js state if any
username: this.props.username ? this.props.username : "",
password: this.props.password ? this.props.password : "",
//this determines whether any fields is filled or not from VerticalLinearStepper
pristine:
this.props.username || this.props.password || !this.props.disabledNext
? false
: true
};
}
passRadioValue = e => {
this.setState({ pristine: false }, () => {
this.props.handleChangeDisabledNext(!e.target.checked);
});
};
handleChange = name => event => {
const value = event.target.value;
this.setState(
{
[name]: value,
pristine: false
},
() => {
this.props.handleChange(name, value); //setState username, password of VerticalLinearStepper.js
}
);
};
resetForm = () => {
this.props.handleChangeDisabledNext(true); //setState disabledNext of VerticalLinearStepper.js
this.setState(
{
username: "",
password: "",
pristine: true
},
() => {
this.props.handleChange("username", "");
this.props.handleChange("password", "");
}
);
this.props.reset();
};
// this.setState({ disabled: !this.state.disabled });
render() {
const { handleSubmit, pristine, reset, submitting } = this.props;
return (
<form onSubmit={handleSubmit}>
<Field
name="username"
type="text"
component={renderField}
label="Username"
initialValue={this.state.username}
onChange={this.handleChange("username")}
/>
<Field
name="password"
type="password"
component={renderField}
label="Password"
initialValue={this.state.password}
onChange={this.handleChange("password")}
/>
<label>
<Field
name="sex"
component={renderField}
type="radio"
value="male"
checked={!this.props.disabledNext}
onChange={this.passRadioValue}
/>{" "}
Male
</label>
<div>
<button type="submit" disabled={submitting}>
Sign Up
</button>
<button
type="button"
disabled={(pristine || submitting) && this.state.pristine} //add state.pristine checking
onClick={this.resetForm}
>
Clear Values
</button>
</div>
</form>
);
}
}
Затем в StepTemplate.js
Добавьте disabledNext, checkDisabledNext
реквизита. checkDisabledNext
, чтобы определить, будет ли кнопка «Далее» иметь условную проверку или нет. disabledNext
является отключенным значением.
const StepTemplate = ({
classes,
canGoBack,
canGoForward,
onBack,
onNext,
text,
children,
//we pass down these 2 values
disabledNext,
checkDisabledNext
}) => (
<Fragment>
<Typography>{text}</Typography>
<div className={classes.actionsContainer}>
<div>
{children}
<Button
disabled={!canGoBack}
onClick={onBack}
className={classes.button}
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={onNext}
className={classes.button}
//determine whether we should check button disabled or not
disabled={checkDisabledNext ? disabledNext : false}
>
{canGoBack ? "Next" : "go to next step"}
</Button>
</div>
</div>
</Fragment>
);
Это Step1.js
, здесь мы просто передаем реквизиты StepTemplate
и AsyncValidationForm
:
const Step = props => (
<StepTemplate
text={`
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
`}
//we want to apply checking on Step1.js, so we add checkDisabledNext attribute
checkDisabledNext={true}
// disabledNext={this.props.disabledNext} //no need to do this because will be passed with {...props} below
{...props}
>
<form>
form for the first step here
<div>test here</div>
<AsyncValidationForm
onSubmit={values => {
console.log(values);
alert(
`Values: username: ${values.username} password: ${values.password}`
);
}}
//these are the props passed down from VerticalLinearStepper.js
handleChangeDisabledNext={props.handleChangeDisabledNext}
disabledNext={props.disabledNext}
handleChange={props.handleChange}
username={props.username}
password={props.password}
/>
</form>
</StepTemplate>
);
Вот исправление проблемы повторного рендеринга:
https://codesandbox.io/s/vqvxj7ky4y
Обновите VerticalLinearStepper.js
, тогда нам больше не нужен файл Step1.js, так как мы записываем содержимое Step1.js в этот файл:
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel";
import StepContent from "@material-ui/core/StepContent";
import Button from "@material-ui/core/Button";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
// import Step1 from "./steps/Step1";
import Step2 from "./steps/Step2";
import Step3 from "./steps/Step3";
import StepTemplate from "./steps/StepTemplate";
import AsyncValidationForm from "./forms/AsyncValidationForm";
const styles = theme => ({
root: {
width: "90%"
},
button: {
marginTop: theme.spacing.unit,
marginRight: theme.spacing.unit
},
actionsContainer: {
marginBottom: theme.spacing.unit * 2
},
resetContainer: {
padding: theme.spacing.unit * 3
}
});
class VerticalLinearStepper extends React.Component {
state = {
activeStep: 0,
//we set our state in this parent
disabledNext: true,
username: "",
password: ""
};
steps = {
//we pass the content of Step1 here, so we dont have to pass props
"Select campaign settings": props => (
<StepTemplate
text={`
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
For each ad campaign that you create, you can control how much you're
willing to spend on clicks and conversions, which networks and
geographical locations you want your ads to show on, and more.
`}
//we want to apply checking on Step1.js, so we add checkDisabledNext attribute
checkDisabledNext={true}
disabledNext={this.state.disabledNext} //use this class' state
{...props}
>
<form>
form for the first step here
<div>test here</div>
<AsyncValidationForm
onSubmit={values => {
console.log(values);
alert(
`Values: username: ${values.username} password: ${
values.password
}`
);
}}
//we use this class setstate , no need to pass down props
handleChangeDisabledNext={this.handleChangeDisabledNext}
disabledNext={this.state.disabledNext}
handleChange={this.handleChange}
username={this.state.username}
password={this.state.password}
/>
</form>
</StepTemplate>
),
"Create an ad group": Step2,
"Create an ad": Step3
};
//setState for disabledNext
handleChangeDisabledNext = value => {
this.setState({ disabledNext: value });
};
//setState for username, password
handleChange = (name, value) => {
this.setState({ [name]: value });
};
stepsCount = () => Object.values(this.steps).length;
canGoBack = () => this.state.activeStep > 0;
canGoForward = () => this.state.activeStep < this.stepsCount();
isFinished = () => this.state.activeStep === this.stepsCount();
handleBack = () => {
if (this.canGoBack()) {
this.setState(prevState => ({ activeStep: prevState.activeStep - 1 }));
}
};
handleNext = () => {
if (this.canGoForward()) {
this.setState(prevState => ({ activeStep: prevState.activeStep + 1 }));
}
};
handleReset = () => this.setState({ activeStep: 0 });
render() {
const { classes } = this.props;
const { activeStep } = this.state;
return (
<div className={classes.root}>
<Stepper activeStep={activeStep} orientation="vertical">
{Object.entries(this.steps).map(([label, CustomStep]) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent>
<CustomStep
canGoBack={this.canGoBack()}
canGoForward={this.canGoForward()}
onBack={this.handleBack}
onNext={this.handleNext}
classes={classes}
/>
</StepContent>
</Step>
))}
</Stepper>
{this.isFinished() && (
<Paper square elevation={0} className={classes.resetContainer}>
<Typography>All steps completed - you're finished</Typography>
<Button onClick={this.handleReset} className={classes.button}>
Reset
</Button>
</Paper>
)}
</div>
);
}
}
VerticalLinearStepper.propTypes = {
classes: PropTypes.object
};
export default withStyles(styles)(VerticalLinearStepper);
Дополнительная ссылка: Реакция: Компонент класса против функционального компонента