尽管使用了 useLayoutEffect,它仍然可以在组件中看到闪烁

Despite using useLayoutEffect, it could still see flickering in the component

我正在使用 react-bootstrapformik

下面的 UpdateWorkloadForm 组件呈现在模态框的主体中。

import React, { useImperativeHandle, useLayoutEffect, useReducer, forwardRef } from 'react';
import PropTypes from 'prop-types';
import { Form, Row, Col } from "react-bootstrap";

import "react-datetime/css/react-datetime.css";

import { useFormik} from 'formik';
import * as yup from "yup";
import Datetime from 'react-datetime';
import moment from 'moment';
import DivSpacing from '../common/divSpacing';
import { reducer } from '../common/reducer'
import { TASKSTATUSOPTIONS } from '../data/constants';

// Initial states of UpdateWorkloadForm
const initialState = {
    workflowTasks: null,
};

const UpdateWorkloadForm = forwardRef((props, ref) => {
    // State Handling
    const [state, dispatch] = useReducer(reducer, initialState);

    /***
     * Rest of the code
     */
    
    // "workflowTask" & "workflowTaskStatus" option selection
    const handleWorkflowTasks = (taskList) => {
        dispatch({ type: "workflowTasks", value: taskList});
        if(props.workloadDetails.task_progression.task_id){
            const statusId = (TASKSTATUSOPTIONS.find(v => { return v.label == props.workloadDetails.task_progression.task_status; })).id;
            formik.setFieldValue("workflowTask", props.workloadDetails.task_progression.task_id);
            formik.setFieldValue("workflowTaskStatus", statusId);
        }else{
            formik.setFieldValue("workflowTask", taskList[0].task_id);
            formik.setFieldValue("workflowTaskStatus", TASKSTATUSOPTIONS[0].id);
        }
    }

    // Fetch the tasks of `workflowDescription` from DB
    const fetchWorkflowTasks = async () => {
        return new Promise(function(resolve, reject){
            props.api.post({
                url: '/fetchWorkflowTasks',
                body: {
                    workflowDescription: props.workloadDetails.Description,
                }
            },  (res) => {
                if(res.tasks){
                    handleWorkflowTasks(res.tasks);
                    resolve();
                }
            }, (err, resp) => {
                reject("Oops! Couldn't fetch tasks from DB.");
            });
        });
    }

    useLayoutEffect(() => {
        if(Object.keys(props.workloadDetails).length !== 0){
            fetchWorkflowTasks();
        }
    },[]);

    return (
        <>
            <Form noValidate onSubmit={formik.handleSubmit}>
                <Row>
                    <Col>
                        <Col>
                            <Form.Group controlId="workflowTask">
                                <Form.Label>Task</Form.Label>
                                <Form.Control
                                    as="select"  
                                    name="workflowTask"
                                    onChange={formik.handleChange}
                                    value={formik.values.workflowTask}
                                    isInvalid={formik.touched.workflowTask && formik.errors.workflowTask}
                                >
                                {   state.workflowTasks &&
                                    state.workflowTasks.map((item, index) => (
                                        <option key={item.task_id} value={item.task_id}>
                                            {item.task_name}
                                        </option>
                                    ))
                                }
                                </Form.Control>
                                <Form.Control.Feedback type="invalid">
                                    {formik.errors.workflowStartingPoint}
                                </Form.Control.Feedback>
                            </Form.Group>
                        </Col>
                    </Col>
                </Row>
                <Row>
                    <Col>
                        <Col>
                            <Form.Group controlId="workflowTaskStatus">
                                <Form.Label>Task Status</Form.Label>
                                <Form.Control
                                    as="select"  
                                    name="workflowTaskStatus"
                                    onChange={formik.handleChange}
                                    value={formik.values.workflowTaskStatus}
                                >
                                {   TASKSTATUSOPTIONS.map((item, index) => (
                                        <option key={item.id} value={item.id}>
                                            {item.label}
                                        </option>
                                    ))
                                }
                                </Form.Control>
                            </Form.Group>
                        </Col>
                    </Col>
                </Row>
            </Form>
            
        </>
    );
})

UpdateWorkloadForm.propTypes = {
    api: PropTypes.object.isRequired,
    workloadDetails: PropTypes.object.isRequired,
};

export default UpdateWorkloadForm;

我的场景是:当模式打开时,我调用 API 来更新表单中的 select 字段。

因为这是DOM突变,所以我用了useLayoutEffect。但我仍然可以看到闪烁的时候 组件已呈现。

我做错了什么?

谢谢

useLayoutEffect 所做的只是,如果您在效果仍为 运行 时设置状态,则生成的渲染将同步完成。它不会等待诸如获取数据之类的异步操作完成。布局效果的典型用例是您想要渲染某些东西,然后立即测量所渲染内容的 on-screen 大小,然后再渲染其他东西,这样用户就看不到 dom 用于测量。

对于获取数据,useLayoutEffect 无济于事。相反,您需要设计您的组件,以便在尚未获取数据时看起来不错。例如,您可以渲染一个加载指示器,或者如果您只想渲染任何内容,您可以 return null 来自组件。

假设你所在的州有一个 .loading 属性,它可能看起来像这样:

if (state.loading) {
  return <div>Loading...</div>
  // or, return null
} else {
  return (
    <>
      <Form noValidate onSubmit={formik.handleSubmit}>
        // ...
      </Form>
    </>
  )
}