尽管使用了 useLayoutEffect,它仍然可以在组件中看到闪烁
Despite using useLayoutEffect, it could still see flickering in the component
我正在使用 react-bootstrap
和 formik
。
下面的 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>
</>
)
}
我正在使用 react-bootstrap
和 formik
。
下面的 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>
</>
)
}