React Native 和 Redux:如何以及在何处使用 componentWillMount()

React Native & Redux : how and where to use componentWillMount()

我使用 react-native 和 redux 开发带有 facebook 登录的应用程序。现在我面临一个问题:

Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.

所以我想我必须在渲染方法之前使用 componentWillMount(),但我不知道如何使用它..

containers/Login/index.js

import React, { Component } from 'react';
import { View, Text, ActivityIndicatorIOS } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../../actions';
import LoginButton from '../../components/Login';
import reducers from '../../reducers';
import { Card, CardSection, Button } from '../../components/common';


class Login extends Component {

  // how sould I use it ?
  componentWillMount() {

  }

  render() {
    console.log(this.props.auth);
    const { actions, auth } = this.props;
    var loginComponent = <LoginButton onLoginPressed={() => actions.login()} />;
    if(auth.error) {
      console.log("erreur");
      loginComponent = <View><LoginButton onLoginPressed={() => actions.login()} /><Text>{auth.error}</Text></View>;
    }
    if (auth.loading) {
      console.log("loading");
      loginComponent = <Text> LOL </Text>;
    }
    return(
      <View>
        <Card>
          <CardSection>
            { auth.loggedIn ? this.props.navigation.navigate('Home') : loginComponent }
          </CardSection>
        </Card>
      </View>
    );
  }
}

function mapStateToProps(state) {
  return {
    auth: state.auth
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(actionCreators, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Login);

减速器:

import { LOADING, ERROR, LOGIN, LOGOUT } from '../actions/types';

function loginReducer(state = {loading: false, loggedIn: false, error: null}, action) {
  console.log(action);
  switch(action.type) {
    case LOADING:
      console.log('Inside the LOADING case');
      return Object.assign({}, state, {
        loading: true
      });
    case LOGIN:
      return Object.assign({}, state, {
        loading: false,
        loggedIn: true,
        error: null,
      });
    case LOGOUT:
      return Object.assign({}, state, {
        loading: false,
        loggedIn: false,
        error: null
      });
    case ERROR:
      return Object.assign({}, state, {
        loading: false,
        loggedIn: false,
        error: action.err
      });
    default:
      return state;
  }

}

export default loginReducer;

和操作:

import {
  LOADING,
  ERROR,
  LOGIN,
  LOGOUT,
  ADD_USER
} from './types';
import { facebookLogin, facebookLogout } from '../src/facebook';

export function attempt() {
  return {
    type: LOADING
  };
}

export function errors(err) {
  return {
    type: ERROR,
    err
  };
}

export function loggedin() {
  return {
    type: LOGIN
  };
}

export function loggedout() {
  return {
    type: LOGOUT
  };
}

export function addUser(id, name, profileURL, profileWidth, profileHeight) {
  return {
    type: ADD_USER,
    id,
    name,
    profileURL,
    profileWidth,
    profileHeight
  };
}

export function login() {
  return dispatch => {
    console.log('Before attempt');
    dispatch(attempt());
    facebookLogin().then((result) => {
      console.log('Facebook login success');
      dispatch(loggedin());
      dispatch(addUser(result.id, result.name, result.picture.data.url, result.picture.data.width, result.data.height));
    }).catch((err) => {
      dispatch(errors(err));
    });
  };
}

export function logout() {
  return dispatch => {
    dispatch(attempt());
    facebookLogout().then(() => {
      dispatch(loggedout());
    })
  }
}

如果您需要更多代码,这里是我的代码库: https://github.com/antoninvroom/test_redux

componentWillMount() 在安装发生之前立即被调用。它在 render() 之前调用,因此在此方法中设置状态不会触发重新渲染。避免在此方法中引入任何副作用或订阅。

如果您需要更多信息 componentWillMount() 阅读此 https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/birth/premounting_with_componentwillmount.html

根据您对 Ajay 的回答的评论,您希望在组件中设置初始状态。为此,您可以在 constructor function.

中设置状态
class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      color: props.initialColor
    };
  }  

如果您有要放置在组件状态中的异步获取的数据,you can use componentWillReceiveProps.

componentWillReceiveProps(nextProps) {
  if (this.props.auth !== nextProps.auth) {
    // Do something if the new auth object does not match the old auth object
    this.setState({foo: nextProps.auth.bar});
  }
}

componentWillMount 是创建组件时第一个 运行 的函数。 getDefaultProps 首先是 运行,然后是 getInitialState,然后是 componentWillMount。只有当您使用 react.createClass 方法创建组件时,getDefaultPropsgetInitialState 才会是 运行。如果组件是 class 扩展 React.Component,那些方法将不会是 运行。如果可以,建议使用 componentDidMount 而不是 componentWillMount 因为你的组件仍然可以在 componentWillMount 和第一次渲染之前更新。

您可以找到有关 React 组件生命周期的更多信息here

此外,建议在 class 构造函数或使用 getDefaultPropsgetInitialState.

中设置状态或默认道具
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { bar: 'foo' };
  }

  static defaultProps = {
    foo: 'bar'
  };
}

编辑: 这是处理登录的组件

import React, { Component } from 'react';
import { View, Text, ActivityIndicatorIOS } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../../actions';
import LoginButton from '../../components/Login';
import reducers from '../../reducers';
import { Card, CardSection, Button } from '../../components/common';

class Login extends Component {
  componentDidMount() {
    // If user is already logged in
    if(this.props.auth.loggedIn) {
      // redirect user here
    }
  }

  componentWillReceiveProps(nextProps) {
    // If the user just log in
    if(!this.props.auth.loggedIn && nextProps.auth.loggedIn) {
      // Redirect user here
    }
  }

  render() {
    console.log(this.props.auth);
    const { actions, auth } = this.props;
    var loginComponent = <LoginButton onLoginPressed={() => actions.login()} />;
    if(auth.error) {
      console.log("erreur");
      loginComponent = <View><LoginButton onLoginPressed={() => actions.login()} /><Text>{auth.error}</Text></View>;
    }
    if (auth.loading) {
      console.log("loading");
      loginComponent = <Text> LOL </Text>;
    }
    return(
      <View>
        <Card>
          <CardSection>
            { auth.loggedIn ? this.props.navigation.navigate('Home') : loginComponent }
          </CardSection>
        </Card>
      </View>
    );
  }
}

function mapStateToProps(state) {
  return {
    auth: state.auth
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(actionCreators, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Login);