static getDerivedStateFromProps需要先前的道具和状态回调吗?

时间:2018-03-07 16:23:26

标签: javascript reactjs react-native

我刚刚更新以反应原生0.54.0,其中还包括反应16.3的alpha 1,我自然会收到很多关于componentWillMountcomponentWillReceiveProps折旧的警告。

我有一个依赖于componentWillReceiveProps核心的动画路线组件,它接收新路径,将其与之前的路径进行比较,如果它们设置了旧子项,将它们设置为动画,设置新子项并将它们设置为动画

以下是有问题的代码:

componentWillReceiveProps(nextProps: Props) {
    if (nextProps.pathname !== this.props.pathname) {
      this.setState({ previousChildren: this.props.children, pointerEvents: false }, () =>
        this.animate(0)
      );
    }
  }

现在我有关于将此移植到static getDerivedStateFromProps

的问题

1)我无法访问this.props,因此无法访问以前的路径名,我想我可以将这些道具存储在状态中,这是正确的方法吗?似乎重复数据。

2)因为我无法访问this我无法调用我的动画功能,而是依赖于状态。我怎么能绕过这个?

3)我需要首先设置状态,然后调用动画,因为getDerivedStateFromProps通过返回值设置状态,之后我不能做很多事情,所以有没有办法设置状态和之后那已经完成了回调?

4)pathname位目前仅用于componentWillreceiveProps,如果我将其移至州并且永远不会在this.state内使用getDerivedStateFromProps(因为我可以'} t)this.state.pathname错误定义但从未使用过。这里最好的方法是使它静止吗?

我的第一直觉是将其更改为componentDidUpdate,但我们不应该在其中使用setState正确吗? (这在我的场景中确实有用)。

  componentDidUpdate(prevProps: Props) {
    if (this.props.pathname !== prevProps.pathname) {
      this.setState({ previousChildren: prevProps.children, pointerEvents: false }, () =>
        this.animate(0)
      );
    }
  }

注意:据我所知,我认为有一个功能正在进入"悬疑"这将允许我们保持视图安装在组件卸载事件?我无法找到任何对此的引用,但这听起来像是我可以用于此动画的东西

对于任何有兴趣的人,这是一个有问题的完整组件的片段

// @flow
import React, { Component, type Node } from "react";
import { Animated } from "react-native";

type Props = {
  pathname: string,
  children: Node
};

type State = {
  animation: Animated.Value,
  previousChildren: Node,
  pointerEvents: boolean
};

class OnboardingRouteAnomation extends Component<Props, State> {
  state = {
    animation: new Animated.Value(1),
    previousChildren: null,
    pointerEvents: true
  };

  componentWillReceiveProps(nextProps: Props) {
    if (nextProps.pathname !== this.props.pathname) {
      this.setState({ previousChildren: this.props.children, pointerEvents: false }, () =>
        this.animate(0)
      );
    }
  }

  animate = (value: 0 | 1) => {
    Animated.timing(this.state.animation, {
      toValue: value,
      duration: 150
    }).start(() => this.animationLogic(value));
  };

  animationLogic = (value: 0 | 1) => {
    if (value === 0) {
      this.setState({ previousChildren: null, pointerEvents: true }, () => this.animate(1));
    }
  };

  render() {
    const { animation, previousChildren, pointerEvents } = this.state;
    const { children } = this.props;
    return (
      <Animated.View
        pointerEvents={pointerEvents ? "auto" : "none"}
        style={{
          alignItems: "center",
          opacity: animation.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }),
          transform: [
            {
              scale: animation.interpolate({ inputRange: [0, 1], outputRange: [0.94, 1] })
            }
          ]
        }}
      >
        {previousChildren || children}
      </Animated.View>
    );
  }
}

export default OnboardingRouteAnomation;

1 个答案:

答案 0 :(得分:12)

使用react版本16.3,使用componentWillReceiveProps很好,但会导致在控制台中显示弃用警告。 It will be removed in version 17。 FWIW Dan Abramov has warned against using alpha functionality in production - 但这是在2018年3月回来的。

让我们来看看你的问题:

  

1)我不再能访问this.props,因此无法访问以前的路径名,我想我可以将这些道具存放在状态中。这是正确的方法吗?似乎重复数据。

是的。

如果要更新/重新渲染组件,则应合并propsstate。在您的情况下,您希望在使用生命周期方法更改pathname时更新组件/触发器动画。

  

似乎重复数据。

检查一下:React Team post on State and Lifecycle

  

2)因为我无法访问这个,所以我无法调用依赖于状态的动画功能。我怎么能绕过这个?

Use componentDidUpdate

  

<强> componentDidUpdate

     

在每个重新渲染周期中渲染完成后,将调用此函数。这意味着您可以确保组件及其所有子组件已正确呈现。

这意味着您可以在animate

中的setState之后致电componentDidUpdate
  

3)我需要先设置状态,然后调用动画,因为getDerivedStateFromProps通过返回值来设置状态,之后我做不了多少,所以有没有办法设置状态,然后执行完成后执行a回调?

参见第2点(Use componentDidUpdate

  

4)pathname位目前仅在componentWillreceiveProps中使用,如果我将其移动到state并且从不在getDerivedStateFromProps中使用this.state(因为我不能)将this.state.pathname错误定义为但从未使用过。这里最好的方法是使它静止吗?

不,它将由componentDidUpdate

使用

以下是我如何做到这一点:

// @flow
import React, { Component, type Node } from "react";

type Props = {
    pathname: string,
    children: Node
};

type State = {
    previousChildren: Node,
    pointerEvents: boolean
};

class OnboardingRouteAnomation extends Component<Props, State> {
    state = {
        previousChildren: null,
        pointerEvents: true,
        pathname: ''
    };

    static getDerivedStateFromProps(nextProps, prevState) {
        if (nextProps.pathname !== prevState.pathname) {
            return {
                previousChildren: nextProps.children,
                pointerEvents: false,
                pathname: nextProps.pathname
            };
        }

        return null;
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.pathname !== this.state.pathname){
            console.log("prevState.pathname", prevState.pathname);
            console.log("this.props.pathname", this.props.pathname);
            this.animate(0);
        }
    }

    componentDidMount(){
        this.setState({ pathname: this.props.pathname});
    }

    animate = (value: 0 | 1) => {
        console.log("this animate called", this);
        animationLogic(value);
    };

    animationLogic = (value: 0 | 1) => {
        if (value === 0) {
            this.setState({ previousChildren: null, pointerEvents: true }, () => this.animate(1));
        }
    };

    render() {
        const { animation, previousChildren, pointerEvents } = this.state;
        const { children } = this.props;
        return (
            <div>
                {this.props.children}
            </div>
        );
    }
}

export default OnboardingRouteAnomation;

我相信这是反应开发者应该如何处理的。你应该  通过componentDidUpdate更新后调用动画,因为它有副作用。

我使用更具描述性的名称来表示路径已更改。像isPathUpdated这样的东西。然后,您可以animate检查isPathUpdated作为切换。