You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Bootstrap注册组件Alert动态显示控制及测试问询

React-Bootstrap 注册组件 Alert 显示控制与测试问题

我用 React-Bootstrap 开发了一个注册组件,表单包含 email、password 输入框和提交按钮,组件末尾有个 Alert 组件,要求初始不可见,提交表单后根据服务器响应显示成功/失败提示。需要解决以下问题:

  1. 如何初始隐藏 Alert?比如动态设置 display:none?
  2. 如何在收到服务器响应后让 Alert 可见?

同时需要编写测试用例:

  1. 验证 Alert 初始不在文档中(现有测试代码是否正确?)
  2. 模拟服务器响应后,验证 Alert 可见且包含正确提示消息(如何实现?)

组件代码(原版本)

import React from 'react';
import {Col, Row, Container, Card,Button, Alert} from 'react-bootstrap'
import Form from 'react-bootstrap/Form';
import { useState } from 'react';
import User from '../models/user';

let Register = () => {

    let [state,setState] = useState(new User('',''));

    let submitresponse='';
    let alertvariant='';

    let updateInput = (e) =>{
        setState({
                ...state, //state defined above in useState call. Expand the current state and then override the propoerty which target represents
                [e.target.name] : e.target.value
        })
    }

    let register = (e) =>{
        e.preventDefault();
        console.log(`registerclick: ${JSON.stringify(state)}`)

        fetch(process.env.REACT_APP_REGISTER_USER_URL,{
            method: 'POST',
            headers:{
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(state)
            }) //move to separate funcion in separate file to make it easy to test and reuse?
            .then((res) => {
                console.log(`got response: ${JSON.stringify(res)}`);
                return res.json();  //return json from res
            })
            .then((data) => { //data is the json returned previously. Promise chaining
                console.log(`data after register user request: ${JSON.stringify(data)}`);  
                if(data.result == "success") {
                    setState(new User(data.user,data.email)) //override previous value of data object with value of received json object. 
                    this.submitresponse = "User Registered"
                } else {
                    setState(new User('','')) //override previous value of data object with value of received json object. 
                    this.submitresponse = "User Couldn't be Registered"
                }     
            });
    }

    return (
        <>
            <Container className='mt-3'>
                <Row>
                    <Col xs={3}>
                        <Card className="shadow-lg">
                            <Card.Header className='p-3'>
                                 Register
                            </Card.Header>
                            <Card.Body>
                                <Form>
                                    <Form.Group className="mb-3">
                                        <Form.Control name="email" onChange={updateInput} type="text" placeholder="email" aria-label="aria-email" />
                                    </Form.Group>
                                    <Form.Group className="mb-3">    
                                        <Form.Control name="password" onChange={updateInput} type="password" placeholder="password" aria-label="aria-password" />
                                    </Form.Group>                                    
                                    <Form.Group className="mb-3">    
                                        <Button onClick={register} type="submit" name="submit-button" aria-label="aria-submit">Register Button</Button>
                                    </Form.Group>
                                    <Form.Group className="mb-3">    
                                        <Alert name="submit-response-alert" key={this.alertvariant} variant={this.alertvariant} >{this.submitresponse}</Alert>
                                    </Form.Group>
                                </Form>
                            </Card.Body>
                        </Card>
                    </Col>
                </Row>
            </Container>
            
        </>
    )
}

export default Register;

现有测试代码片段

1. 初始 Alert 验证

test('Should not show Alert field', async () => {
render();
expect(screen.getByName("submit-response-alert")).not.toBeInTheDocument(); //Is this the right way?
});

2. 提交后 Alert 验证

test('Should successfully submit the form', async () => {
const { getByText } = render();

 await act(async () => {
   fireEvent.click(getByText("Register Button"))
 })  
 expect(screen.getByName("submit-response-alert")).toBeInTheDocument();
//how to test that the Alert has the right message
});

问题解答与代码修正

一、Alert 显示/隐藏的组件代码修正

原组件存在两个核心问题:函数组件误用 this 访问变量、Alert 未做显示控制。以下是修正后的完整代码:

import React, { useState } from 'react';
import { Col, Row, Container, Card, Button, Alert } from 'react-bootstrap';
import Form from 'react-bootstrap/Form';
import User from '../models/user';

const Register = () => {
  const [state, setState] = useState(new User('', ''));
  // 新增状态控制 Alert 的显示状态、内容和样式
  const [alertState, setAlertState] = useState({
    show: false,
    message: '',
    variant: ''
  });

  const updateInput = (e) => {
    setState({
      ...state,
      [e.target.name]: e.target.value
    });
  };

  const register = (e) => {
    e.preventDefault();
    console.log(`registerclick: ${JSON.stringify(state)}`);

    fetch(process.env.REACT_APP_REGISTER_USER_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(state)
    })
      .then((res) => res.json())
      .then((data) => {
        console.log(`data after register user request: ${JSON.stringify(data)}`);
        if (data.result === "success") {
          setState(new User(data.user, data.email));
          setAlertState({
            show: true,
            message: "User Registered",
            variant: 'success'
          });
        } else {
          setState(new User('', ''));
          setAlertState({
            show: true,
            message: "User Couldn't be Registered",
            variant: 'danger'
          });
        }
      })
      .catch(err => {
        // 处理请求失败场景
        setAlertState({
          show: true,
          message: "请求失败,请稍后重试",
          variant: 'danger'
        });
      });
  };

  return (
    <Container className='mt-3'>
      <Row>
        <Col xs={3}>
          <Card className="shadow-lg">
            <Card.Header className='p-3'>Register</Card.Header>
            <Card.Body>
              <Form onSubmit={register}>
                <Form.Group className="mb-3">
                  <Form.Control
                    name="email"
                    onChange={updateInput}
                    type="email"
                    placeholder="email"
                    aria-label="aria-email"
                  />
                </Form.Group>
                <Form.Group className="mb-3">
                  <Form.Control
                    name="password"
                    onChange={updateInput}
                    type="password"
                    placeholder="password"
                    aria-label="aria-password"
                  />
                </Form.Group>
                <Form.Group className="mb-3">
                  <Button type="submit" name="submit-button" aria-label="aria-submit">
                    Register Button
                  </Button>
                </Form.Group>
                {/* 条件渲染控制 Alert 显示:初始不渲染,响应后渲染 */}
                {alertState.show && (
                  <Form.Group className="mb-3">
                    <Alert
                      name="submit-response-alert"
                      variant={alertState.variant}
                    >
                      {alertState.message}
                    </Alert>
                  </Form.Group>
                )}
              </Form>
            </Card.Body>
          </Card>
        </Col>
      </Row>
    </Container>
  );
};

export default Register;

关键说明:

  1. 初始隐藏 Alert:

    • 通过 alertState.show 做条件渲染,初始 show: false 时不会渲染 Alert,自然不在 DOM 中
    • 若需保留 DOM 仅隐藏,可改用样式控制:<Alert style={{ display: alertState.show ? 'block' : 'none' }} ... />,但条件渲染更高效
  2. 响应后显示 Alert:

    • 在 fetch 的回调中,根据服务器返回结果调用 setAlertState,将 show 设为 true,同时设置对应提示消息和样式变体(success/danger)

二、测试用例修正与实现

1. 验证 Alert 初始不在文档中

原测试代码错误:screen.getByName 不是 React Testing Library 标准方法,且初始不存在的元素应使用 queryBy(getBy 找不到会报错)。修正后:

import { render, screen } from '@testing-library/react';
import Register from './Register';

test('初始状态下不显示 Alert', () => {
  render(<Register />);
  // queryByRole 找不到元素返回 null,不会抛出错误
  const alert = screen.queryByRole('alert');
  expect(alert).not.toBeInTheDocument();
});

2. 模拟服务器响应后验证 Alert 状态

需先模拟 fetch 请求避免调用真实 API,再触发提交并验证 Alert:

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import Register from './Register';

// 模拟成功响应
test('提交成功后显示成功提示的 Alert', async () => {
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ result: "success", user: "testUser", email: "test@example.com" })
    })
  );

  render(<Register />);
  fireEvent.click(screen.getByText('Register Button'));

  // 等待异步操作完成,验证 Alert 状态
  await waitFor(() => {
    const successAlert = screen.getByRole('alert');
    expect(successAlert).toBeInTheDocument();
    expect(successAlert).toHaveTextContent('User Registered');
    expect(successAlert).toHaveClass('alert-success');
  });

  jest.restoreAllMocks();
});

// 模拟失败响应
test('提交失败后显示失败提示的 Alert', async () => {
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ result: "fail" })
    })
  );

  render(<Register />);
  fireEvent.click(screen.getByText('Register Button'));

  await waitFor(() => {
    const errorAlert = screen.getByRole('alert');
    expect(errorAlert).toBeInTheDocument();
    expect(errorAlert).toHaveTextContent("User Couldn't be Registered");
    expect(errorAlert).toHaveClass('alert-danger');
  });

  jest.restoreAllMocks();
});

测试关键说明:

  • 用 jest.fn() 模拟 global.fetch,返回预设的成功/失败响应
  • 使用 waitFor 等待异步请求完成,确保 Alert 已渲染
  • 通过 getByRole('alert') 获取元素,验证其存在性、文本内容和样式类

内容的提问来源于stack exchange,提问作者Manu Chadha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 04:16:03