使用 getDerivedStateFromProps 获取 API 数据导致组件渲染多次

Non*_*Non 3 javascript ecmascript-6 reactjs

我正在尝试从 API 调用中获取一些数据。我正在使用getDerivedStateFromPropscomponentDidMount,shouldComponentUpdate andcomponentDidUpdate`。

我这样做是因为我需要userTokenuserToken: store.signinScreen.userToken调用GetPassengersData需要userToken从 API 获取数据的函数之前使用它。

这是整个组件:

// imports

class HomeScreen extends Component {
  static navigationOptions = {
    header: null,
  };

  state = {
    error: false,
  };

  static getDerivedStateFromProps(props, state) {
    if (props.userToken !== state.userToken) {
      return { userToken: props.userToken };
    }
    return null;
  }

  componentDidMount() {
    this.GetPassengersData();
  }

  shouldComponentUpdate(prevProps, state) {
    return this.props !== prevProps;
  }

  componentDidUpdate(prevProps, prevState) {
    const { error } = this.state;
    if (!error) {
      this.GetPassengersData();
    }
  }

  GetPassengersData = async () => {
    const { passengersDataActionHandler, userToken } = this.props;
    if (userToken && userToken !== null) {
      try {
        const response = await fetch(
          'http://myAPI/public/api/getPassengers',
          {
            method: 'POST',
            headers: {
              Authorization: `Bearer ${userToken}`,
              Accept: 'application/json',
              'Content-Type': 'application/json',
            },
          },
        );
        const responseJson = await response.json();
        if (has(responseJson, 'error')) {
          this.setState({ error: true });
          Alert.alert('Error', 'Please check your credentials.');
        } else {
          passengersDataActionHandler(responseJson.success.data);
        }
      } catch (error) {
        this.setState({ error: true });
        Alert.alert(
          'Error',
          'There was an error with your request, please try again later.',
        );
      }
    }
  };

  render() {
    return <TabView style={styles.container} />;
  }
}

HomeScreen.defaultProps = {
  userToken: null,
};

HomeScreen.propTypes = {
  navigation: PropTypes.shape({}).isRequired,
  passengersDataActionHandler: PropTypes.func.isRequired,
  userToken: PropTypes.oneOfType([PropTypes.string]),
};

export default compose(
  connect(
    store => ({
      userToken: store.signinScreen.userToken,
      passengersData: store.homeScreen.passengersData,
    }),
    dispatch => ({
      passengersDataActionHandler: token => {
        dispatch(passengersDataAction(token));
      },
    }),
  ),
)(HomeScreen);
Run Code Online (Sandbox Code Playgroud)

通过此实现,组件会渲染多次,因此会破坏应用程序。

我可能做错了什么?

Shu*_*tri 5

首先,您不需要存储userToken在状态中,因为您没有在本地修改它,因此您不需要getDerivedStateFromProps

其次,您需要仅在 prop 更改时触发 componentDidUpdate 中的 API 调用,而不是在没有检查的情况下直接触发,否则 API 中的 setState 成功或错误将导致组件再次重​​新渲染调用componentDidUpdate,从而再次调用 API 导致无限循环

第三,shouldComponentUpdate 内部比较 props 的检查并不完全正确,因为nestedObjects props 会给出假阴性结果,而且如果您为 props 编写深度相等性检查,则如果状态发生变化,组件将不会重新渲染。

// imports

class HomeScreen extends Component {
  static navigationOptions = {
    header: null,
  };

  state = {
    error: false,
  };

  componentDidMount() {
    this.GetPassengersData();
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps.userToken !== this.props.userToken) {
      this.GetPassengersData();
    }
  }

  GetPassengersData = async () => {
    const { passengersDataActionHandler, userToken } = this.props;
    if (userToken && userToken !== null) {
      try {
        const response = await fetch(
          'http://myAPI/public/api/getPassengers',
          {
            method: 'POST',
            headers: {
              Authorization: `Bearer ${userToken}`,
              Accept: 'application/json',
              'Content-Type': 'application/json',
            },
          },
        );
        const responseJson = await response.json();
        if (has(responseJson, 'error')) {
          this.setState({ error: true });
          Alert.alert('Error', 'Please check your credentials.');
        } else {
          passengersDataActionHandler(responseJson.success.data);
        }
      } catch (error) {
        this.setState({ error: true });
        Alert.alert(
          'Error',
          'There was an error with your request, please try again later.',
        );
      }
    }
  };

  render() {
    return <TabView style={styles.container} />;
  }
}

HomeScreen.defaultProps = {
  userToken: null,
};

HomeScreen.propTypes = {
  navigation: PropTypes.shape({}).isRequired,
  passengersDataActionHandler: PropTypes.func.isRequired,
  userToken: PropTypes.oneOfType([PropTypes.string]),
};

export default compose(
  connect(
    store => ({
      userToken: store.signinScreen.userToken,
      passengersData: store.homeScreen.passengersData,
    }),
    dispatch => ({
      passengersDataActionHandler: token => {
        dispatch(passengersDataAction(token));
      },
    }),
  ),
)(HomeScreen);
Run Code Online (Sandbox Code Playgroud)