如何将Redux动作转换为异步动作

时间:2019-01-11 19:39:14

标签: reactjs redux

以下代码似乎有些出入,有时可以工作。像它那样工作将呈现数据列表,而不像我那样遇到forEach错误,因为this.props.getPosts();尚未加载。

我需要一种方法来告诉反应以获取this.props.getPosts()然后为每个循环执行操作,而if语句则不会。我需要使动作异步。诸如等待获取之类的东西。不确定如何在操作方法中进行操作。

我也遇到了这个错误,我想如果我使我的动作getPosts异步,它将解决这个错误。

  

index.js:1446警告:无法在服务器上执行React状态更新   未安装的组件。这是空操作,但表示内存泄漏   在您的应用程序中。要修复,请取消所有订阅并异步   componentWillUnmount方法中的任务。

这是我目前拥有的

App.js

const styles = {
  card: {
    minWidth: 275,
    margin:'40px 0px',

  },
  p:{
      margin:'20px 0px',
      letterSpacing: '2.7px',
      fontSize:'0.8em',
      fontStyle: 'italic'
  },
  h:{
    letterSpacing: '5px' 
  }
};

const equalArrays = (arr1, arr2) => {
    if(arr1.length !== arr2.length)
        return false;
    for(var i = arr1.length; i--;) {
        if(arr1[i] !== arr2[i])
            return false;
    }
    return true;
}

class App extends Component{

    constructor(props){
        super(props)

        this.state = {
            username:"",
            loading: true,
            posts:[]
        }

    }

    componentDidUpdate(prevProps) {
        const prevMyPosts = prevProps.myPosts;
        const myPosts = this.props.myPosts;

        if (!equalArrays(prevMyPosts, myPosts)) {
            this.setState({ posts: myPosts })
        }
    }


    componentDidMount(){
        if(this.props.userId){
            const collection = fire.collection('users');
            collection.get().then(snapshot => {     
              snapshot.forEach(doc => { 
                this.setState({
                    username: doc.data().username,
                    loading:false
                })                 
              });   
            });

        }

        this.props.getPosts();

    }

    render(){
        if (!this.props.userId) return <Redirect to='/' />
        const { loading, posts } = this.state;

        if(loading){
           return(
            <div className="loader"></div>
           ) 
        }
        return(
            <div className="container"> 
                <div className="row">
                    <div className="col-md-6 mt-3">
                        <h1>Welcome {this.state.username.toLowerCase()}</h1>

                        {posts.map((post, key)=> {
                            return(
                                 <Card key={key} style={styles.card}>
                                        <CardContent>

                                        <Typography variant="h4" component="h2" style={styles.h}>
                                            {post.description}
                                        </Typography>
                                        <Typography component="p" style={styles.p}>
                                            by: {post.username}
                                        </Typography>

                                        <Typography component="p">
                                            by: {moment(post.createdAt.toDate()).calendar()}
                                        </Typography>

                                    </CardContent>
                                </Card>
                            ); 
                        })} 
                    </div>
                </div>
            </div>


        );
    }


}

const mapStateToProps = (state) => ({
    user: state.auths.user,
    userId: state.auths.userId,
    myPosts: state.auths.myPosts
})

const mapDispatchToProps = (dispatch) => ({
    getPosts: () => dispatch(getPosts())
})

export default withRouter(connect(mapStateToProps,  mapDispatchToProps)(App));

Actions.js (我想使该操作异步,不确定如何操作)

const _getPosts = (posts) => ({
    type: 'GET_POSTS',
    posts
})
export const getPosts = () => { return(dispatch) =>{
    return fire.collection('posts').get().then(snapshot => {
        const posts = [];

        snapshot.forEach(item => {
            posts.push(item.data());
        });

        // console.log(posts)

        dispatch(_getPosts(posts));
    })

 }
}

Reducers.js (存储myPosts数据的地方)

import { SET_USER} from '../actions/';
const initialState = {
    authError: null,
    isAuthenticated: false,
    userId: null,
    user: {},
    myPosts:[]
}

export default (state = initialState, action) => {
    switch (action.type) {
        case SET_USER:
            return ({
                ...state
                userId: action.payload.uid || null,
                // user:action.payload,
                isAuthenticated: true
            })
        case 'LOGOUT_SUCCESS':
            console.log('signout success')
            return ({
                ...state,
                userId: null,
                isAuthenticated: false
            })   
        case 'GET_POSTS':
            return ({
                ...state,
                myPosts: action.posts
            })


        case 'CREATE_POST': 
            console.log('created post', action.post)
            return state;

        case 'CREATE_POST_ERROR':
            console.log('create post error', action.err)
            return state;   

        case 'SIGNUP_SUCCESS':      
            return ({
                ...state,
                authError: null
            })

        case 'SIGNUP_ERROR':
            console.log('signup error')
            return ({
                ...state,
                authError: action.err.message
            })

        case 'SIGNIN_SUCCESS':
            console.log('signin success')
            return ({
                ...state,
                authError: null
            })

        case 'SIGNIN_ERROR':
            console.log('signin error')
            return ({
                ...state,
                authError: action.err.message
            })
        default:
            return state
    }
}

1 个答案:

答案 0 :(得分:0)

使redux动作异步不会解决您的问题,最终将冻结UI。相反,不要使用componentWillMount,而是在getPosts中调用componentDidMount并使用componentDidUpdate来更新状态。假设您的组件已连接到redux存储,则可以执行以下操作:

const equalArrays = (arr1, arr2) => {
    if(arr1.length !== arr2.length)
        return false;
    for(var i = arr1.length; i--;) {
        if(arr1[i] !== arr2[i])
            return false;
    }
    return true;
}

class YourComponent extends React.Component {
    constructor(props){
        super(props)
        // this attr will help get rid of memory leak warning.
        this._isMounted = false; 
        this.state = {
            username: "",
            loading: true,
            posts:[]
        }
    }

    componentDidMount() {
        this._isMounted = true;

        if(this.props.userId) {
            const collection = fire.collection('users');
            collection.get().then(snapshot => {     
                // If the component is unmounted, this block
                // can still be executed, causing a memory leak
                // (hence the warning).
                // We can fix it by checking the value of `this._isMounted`.
                if (!this._isMounted) { return }
                snapshot.forEach(doc => {
                    this.setState({
                        username: doc.data().username,
                        loading:false
                    })                 
                });   
            });
        }

        this.props.getPosts();
    }

    componentWillUnmount() {
        this._isMounted = false;
    }

    componentDidUpdate(prevProps) {
        const prevMyPosts = prevProps.myPosts;
        const myPosts = this.props.myPosts;
        if (prevMyPosts !== undefined && !equalArrays(prevMyPosts, myPosts)) {
            this.setState({ posts: myPosts })
        }
    }

    render() { ... }
}

const mapStateToProps = state => ({
    myPosts: state.myPosts,
});

const mapDispatchToProps = dispatch => ({
    getPosts: () => dispatch(getPosts()),
});

YourComponent = connect(
    mapStateToProps,
    mapDispatchToProps,
)(YourComponent);

请注意,更新React组件状态的方式是通过setState,而不是直接。

另外,请注意,必须以this.setState中的条件包装componentDidUpdate;否则会导致无限循环。更多详细信息here

以上方法之所以有效,是因为当您传递给道具的道具发生更改时,与Redux连接的组件将重新渲染,从而触发componentDidUpdate