【问题标题】:Set initial state for material ui dialog设置材质 ui 对话框的初始状态
【发布时间】:2020-02-01 05:19:02
【问题描述】:

我有一个MaterialUI dialog,上面有一些文本字段、下拉菜单和其他内容。每次打开或重新打开对话框时,都需要将其中一些元素设置为某个值。在某些条件存在(例如,加载用户数据)之前,无法加载其他元素。

对于“重置”,我使用的是 onEnter 函数。但是 onEnter 函数在进入之前不会运行(呃!)......但是渲染函数本身仍然会运行 - 这意味着仍然会发生任何逻辑或访问 JSX 中的 javascript 变量。这使得“onEnter”函数无法成为我设置和初始化对话框的地方。

我也不能使用构造函数来设置/重置这个初始状态,因为在构造函数加载时(在应用程序启动时)我需要构造状态的数据可能不可用。现在,我可以在我的渲染函数中让 JSX 变得超级复杂,并为每个数据点设置条件......但这对于每次应用程序更改任何内容时都会重新渲染的东西来说是很多开销。 (即使 'open' 参数设置为 false,出现的材质 UI 对话框也会运行整个渲染功能)。

为材质 ui 对话框处理初始化值的最佳方法是什么?

这是一个超级简单的例子(在现实生活中,想象 getInitialState 是一个更复杂、更慢、并且可能是异步/网络的函数)——让我们假设用户对象在应用程序开始时不可用并且是实际上,一些数据在应用程序启动后很长时间才被提取或输入。此代码失败,因为“用户”在第一次渲染时未定义(发生在 onEnter 运行之前)。

constructor(props) {
    super(props);
}

getInitialState = () => {
    return {
        user: {username: "John Doe"}
    }
}

onEnter = () => {
    this.setState(this.getInitialState())
}


render() {
    const { dialogVisibility } = this.props;

    return (
        <Dialog  open={dialogVisibility}  onEnter={this.onEnter}>
            <DialogTitle>
                Hi, {this.state.user.username}
            </DialogTitle>
        </Dialog> );
}

我的第一个直觉是在状态中放入一个“isInitialized”变量,并且只有在“isInitialized”为真时才让渲染返回对话框,如下所示:

constructor(props) {
    super(props);
    this.state = {
        isInitialized: false
    };
}

getInitialState = () => {
    return {
        user: {username: "John Doe"}
    }
}

onEnter = () => {
    this.setState(this.getInitialState(), 
        () => this.setState({isInitialized:true})
    );
}


render() {
    const { dialogVisibility } = this.props;

    if(!this.state.isInitialized) {
        return null;
    }

    return (
        <Dialog  open={dialogVisibility}  onEnter={this.onEnter}>
            <DialogTitle>
                Hi, {this.state.user.username}
            </DialogTitle>
        </Dialog> );
}

我相信您知道...这不起作用,因为我们从未返回 Dialog 以触发 onEnter 事件,而该事件又触发 onEnter 函数并实际初始化数据。我尝试将 !this.state.inInitialized 条件更改为:

    if(!this.state.isInitialized) {
        this.onEnter();
        return null;
    }

这行得通...但它给了我一个运行时警告:Warning: Cannot update during an existing state transition (such as withinrender). Render methods should be a pure function of props and state.

这让我读了很多书,特别是这个问题:Calling setState in render is not avoidable 这真的让我回家了,我不应该只是忽略这个警告。此外,此方法导致返回 JSX 中包含的所有逻辑仍然发生......即使对话框没有“打开”。添加一堆复杂的对话框会降低性能。

当然有一种“正确”的方法可以做到这一点。帮助?想法?

【问题讨论】:

    标签: javascript reactjs material-ui


    【解决方案1】:

    这可以通过保留构造函数、getInitialState 和 onEnter 函数原样并在渲染函数中添加以下三元条件来解决:

    render() {
        const { dialogVisibility } = this.props;
    
    return (
        <Dialog  open={dialogVisibility}  onEnter={this.onEnter}>
            {this.state.isInitialized && dialogVisibility ? 
            <DialogTitle>
                Hi, {this.state.user.username}
            </DialogTitle> : 'Dialog Not Initialized'}
        </Dialog> );
    )}
    

    它实际上允许对话框适当地使用它的“onEnter”,获得正确的转换,并避免在呈现不可见时在 JSX 中运行任何扩展的复杂逻辑。它也不需要重构或增加编程复杂性。

    ...但是,我承认,这感觉非常“错误”。

    【讨论】:

      【解决方案2】:

      你应该使用 componentDidUpdate()

      • 初始渲染不调用此方法
      • 当组件更新时,以此为契机对 DOM 进行操作

      如果需要在对话框打开前预加载数据,可以使用componentDidMount()

      • 在组件安装后立即调用(插入到树中)
      • 如果您需要从远程端点加载数据,这是一个实例化网络请求的好地方

      React 人员为您所描述的情况添加了 useEffect 挂钩,但您需要重构为功能组件。 来源:https://reactjs.org/docs/hooks-effect.html

      【讨论】:

        【解决方案3】:

        您在概念上需要的是,当您刚打开对话框时,您想要重置一些项目。因此,您希望能够监听 open 的值何时从 false 更改为 true

        对于挂钩,react guide 提供了一个使用usePrevious 挂钩保持给定项目的“旧”值的示例。然后只需使用useEffect

        function usePrevious(value) {
          const ref = useRef();
          useEffect(() => {
            ref.current = value;
          });
          return ref.current;
        }
        
        function MyDialog({ dialogVisibility }) {
          const prevVisibility = usePrevious(dialogVisibility);
        
          useEffect(() => {
            // If it is now open, but was previously not open
            if (dialogVisibility && !prevVisibility) {
              // Reset items here
            }
          }, [dialogVisibility, prevVisibility]);
        
          return <Dialog open={dialogVisibility}></Dialog>;
        }
        

        如果你使用componentDidUpdate 和它接收的previousProps 参数,类也可以实现同样的效果。

        export class MyDialog extends Component {
          public componentDidUpdate({ dialogVisibility : prevVisibility }) {
            const { dialogVisibility } = this.props;
        
            if (dialogVisibility && !prevVisibility) {
              // Reset state here
            }
          }
        
          public render() {
            const { dialogVisibility } = this.props;
        
            return <Dialog open={dialogVisibility}></Dialog>;
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-05
          • 1970-01-01
          • 2017-06-20
          • 1970-01-01
          • 2021-02-12
          • 2021-02-16
          • 1970-01-01
          • 2019-04-23
          相关资源
          最近更新 更多