简体   繁体   English

反应条件渲染

[英]React conditionally rendering

I have a simple form build from React.我有一个来自 React 的简单表单构建。 Upon submission, if the error comes back, I wish to render an extra div to show error on the form.提交后,如果错误再次出现,我希望渲染一个额外的 div 以在表单上显示错误。

Right now I got it to work but I do not like the solution.现在我让它工作了,但我不喜欢这个解决方案。 My solution is based on the knowledge that the component will only re-render if the state changes inside the render function (In this case this.state.errorMessages).我的解决方案是基于这样的知识:如果渲染函数内部的状态发生变化,组件只会重新渲染(在这种情况下是 this.state.errorMessages)。 So I had to explicitly put the if condition inside the render function like so所以我必须像这样明确地将 if 条件放在渲染函数中

renderError() {
    var errArr = [];
    for (var key in this.state.errorMessages) {
        errArr = [...errArr, ...this.state.errorMessages[key]];
    }
    return (
        <div className="alert alert-danger">
            {errArr.map((err) => {
                return <p>{err}</p>
            })}
        </div>
    )
}

renderForm() {
    return (
        <form onSubmit={this.handleRegisterFormSubmit}>
            <div className="form-group">
                <label>First Name</label>
                <input type="text" className="form-control" name="name" placeholder="Name" value={this.state.firstName} onChange={this.handleFirstNameChange} required/>
            </div>
            <div className="form-group">
                <label>Last Name</label>
                <input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleLastNameChange} required/>
            </div>
            <div className="form-group">
                <label>Email address</label>
                <input type="email" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" value={this.state.email} onChange={this.handleEmailChange} />
                <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
            </div>
            <div className="form-group">
                <label>Password</label>
                <input type="password" className="form-control" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange}/>
            </div>
            <div className="form-group">
                <label>Password Confirmation</label>
                <input type="password" className="form-control" name="password_confirmation" placeholder="Password Confirmation" value={this.state.passwordConfirmation} onChange={this.handlePasswordConfirmationChange}/>
            </div>
            <div>
                <button type="submit" className="btn btn-primary">Submit</button>
                <button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>Cancel</button>
            </div>
        </form>
    )
}

render () {
    if (!this.state.errorMessages) {
        return (
            <div>
                {this.renderForm()}
            </div>
        )
    } else {
        return (
            <div>
                {this.renderForm()}
                {this.renderError()}
            </div>
        )
    }
}

I don't really like this approach as this could get nasty if I have more condition to re-render.我真的不喜欢这种方法,因为如果我有更多的条件要重新渲染,这可能会变得很糟糕。 I'm hoping there is a solution along the line of not having much logic in the actual render function and have that extracted out.我希望有一个解决方案,即在实际渲染函数中没有太多逻辑并将其提取出来。 For example...例如...

renderError() {
    if (!this.state.errorMessages) {
        return;
    }
    var errArr = [];
    for (var key in this.state.errorMessages) {
        errArr = [...errArr, ...this.state.errorMessages[key]];
    }
    return (
        <div className="alert alert-danger">
            {errArr.map((err) => {
                return <p>{err}</p>
            })}
        </div>
    )
}

render () {
    <form onSubmit={this.handleRegisterFormSubmit}>
        <div className="form-group">
            <label>First Name</label>
            <input type="text" className="form-control" name="name" placeholder="Name" value={this.state.firstName} onChange={this.handleFirstNameChange} required/>
        </div>
        <div className="form-group">
            <label>Last Name</label>
            <input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleLastNameChange} required/>
        </div>
        <div className="form-group">
            <label>Email address</label>
            <input type="email" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" value={this.state.email} onChange={this.handleEmailChange} />
            <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
        </div>
        <div className="form-group">
            <label>Password</label>
            <input type="password" className="form-control" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange}/>
        </div>
        <div className="form-group">
            <label>Password Confirmation</label>
            <input type="password" className="form-control" name="password_confirmation" placeholder="Password Confirmation" value={this.state.passwordConfirmation} onChange={this.handlePasswordConfirmationChange}/>
        </div>
        <div>
            <button type="submit" className="btn btn-primary">Submit</button>
            <button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>Cancel</button>
        </div>
        {this.renderError}
    </form>
}

This throws an error because it complains that this.renderError should be called as a function.这会引发错误,因为它抱怨 this.renderError 应该作为函数调用。 But when I put it as this.renderError() The error will never render because it does not get automatically called when errors come back.但是当我把它作为 this.renderError() 时,错误将永远不会呈现,因为当错误回来时它不会自动调用。

-----------Update---------- - - - - - -更新 - - - - -

Alternatively, why can't I do something like the following或者,为什么我不能执行以下操作

render () {
    <div>
        <form onSubmit={this.handleRegisterFormSubmit}>
            ...
        </form>
        {if (this.state.errorMessages) {this.renderError()}}
    </div>
}

This throws console error这会引发控制台错误

Uncaught Error: Module build failed: SyntaxError: Unexpected token (117:13)未捕获的错误:模块构建失败:语法错误:意外的令牌 (117:13)

----------Update 2----------- ----------更新2-----------

Essentially, I'm looking for a solution where inside the render function, I can easily show a whole block of code when the state changes.本质上,我正在寻找一种解决方案,其中在渲染函数内部,当状态发生变化时,我可以轻松地显示整个代码块。 In Vue, I can do something like在 Vue 中,我可以做类似的事情

<form>
    <input type="text">
</form>
<div v-if="hasError">
    <div class="alert alert-danger">{{something}}</div>
</div>

Can I do something as easy as this in React?

You can just use map in order to extract the error message from your object.您可以只使用 map 来从您的对象中提取错误消息。

Here below a minimal example of form validations and errors in React.下面是 React 中表单验证和错误的最小示例。 It's good to understand how it works, but for my part, I use Formik which simplifies this process.了解它的工作原理很好,但就我而言,我使用Formik简化了这个过程。

class Test extends React.Component {
    constructor(props) {
        super(props);
        this.state = { errorMessages: {} };
    }

    handleRegisterFormSubmit = e => {
        e.preventDefault(); // don't submit the form until we run what's below
        let errorMessages = {};

        if (!this.state.lastName) errorMessages.lastName = 'You need to enter your last name';
        // And so on for each field validation

        // Do we have errors ?
        if (Object.keys(errorMessages).length > 0) {
            this.setState(errorMessages);
        } else {
            // Submit to server
        }
    };

    handleChange = e => {
        this.setState({
            [e.target.name]: e.target.value,
            errorMessages: {
                ...this.state.errorMessages,
                [e.target.name]: null // remove the error of this field if it's being edited
            }
        });
    };

    render() {
        const errArr = Object.keys(this.state.errorMessages).map(key => this.state.errorMessages[key]);
        return (
            <form onSubmit={this.handleRegisterFormSubmit}>
                <div className="form-group">
                    <label>Last Name</label>
                    <input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleChange} />
                </div>

                {/* ... your dom */}

                <div>
                    <button type="submit" className="btn btn-primary">
                        Submit
                    </button>
                    <button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>
                        Cancel
                    </button>
                </div>

                {errArr.length > 0 && (
                    <div className="alert alert-danger">
                      {errArr.map(err => {
                        return <p>{err}</p>;
                      })}
                    </div>
                )}
            </form>
        );
    }
}

One more way to not display your alert div is with a ternary operator for your className and using bootstrap's d-none不显示警报 div 的另一种方法是为 className 使用三元运算符并使用 bootstrap 的d-none

<div className={errArr.length ? "alert alert-danger" : "d-none"}>
    {errArr.map(err => {
        return <p>{err}</p>;
    })}
</div>

I believe this is an architectural question.我相信这是一个架构问题。

Try to follow those practices:尝试遵循这些做法:

1- Inject conditional statement inside JSX directly 1- 直接在 JSX 中注入条件语句

2- Use functional components to render JSX not object methods 2- 使用功能组件来呈现 JSX 而非对象方法


1- Inject conditional statement inside JSX directly 1-直接在 JSX 中注入条件语句

Bad:坏的:

if (!this.state.errorMessages) {
    return (
        <div>
            {this.renderForm()}
        </div>
    )
} else {
    return (
        <div>
            {this.renderForm()}
            {this.renderError()}
        </div>
    )
}

Good:好的:

return <div>
            {this.renderForm()}
            {this.state.errorMessages && this.renderError()}
        </div>

2- Use functional components to render JSX not object methods 2- 使用功能组件来呈现 JSX 而非对象方法

Bad:坏的:

class FormComponent {
  // ....

  renderError() {
    // Blah blah blah 
    return (
        <div className="alert alert-danger">
            {errArr.map((err) => {
                return <p>{err}</p>
            })}
        </div>
    )
   }

   render() {
     return (
       <div>
         <AnotherComponent />
         {this.renderError()}
      <div/>
     )
   }

}

then {this.renderError()}然后{this.renderError()}

Good好的

class FormComponent {
  // ....

   render() {
     return (
       <div>
         <AnotherComponent />
         {<Error />} {/* render it as componet ⚠️*/} 
      <div/>
     )
   }

}
// Build it as component outside the main component (FormComponent) ⚠️
function Error(props) { 
    return (
        <div className="alert alert-danger">
            {props.errArr.map((err) => {
                return <p>{err}</p>
            })}
        </div>
     )

 }

I spent many 2 years in React development of enterprise apps also graduating from Udacity and I am sharing my experience here.我也从 Udacity 毕业,在 React 开发企业应用程序方面花了很多 2 年时间,我在这里分享我的经验。 Congrats!恭喜!

One would usually render items in an element representing the list with some styling applied.人们通常会在表示列表的元素中呈现项目,并应用一些样式。 We also sometimes don't want this wrapping element if we don't have any items.如果我们没有任何物品,我们有时也不想要这个包装元素。

I've written the following component which allows conditional rendering.我编写了以下允许条件渲染的组件。 When the condition is false, no elements are rendered keeping the DOM clean.当条件为假时,不会渲染任何元素以保持 DOM 干净。

export default class If extends Component<{ condition: any }, {}> {

    render() {
        return this.props.condition ? this.props.children : <Fragment></Fragment>;
    }

}

One can now simply use the component as follow:现在可以简单地使用该组件如下:

<If condition={items.length}>
    <-- item list -->
</If>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM