как преобразовать избыточное действие в асинхронное действие - PullRequest
0 голосов
/ 11 января 2019

Кажется, есть некоторое несоответствие со следующим кодом, иногда он работает. Работайте так, как он отображает список данных, а не так, как я получаю ошибку forEach, потому что this.props.getPosts(); еще не загружен.

Мне нужен способ сказать, реагировать на выборку this.props.getPosts(), а затем выполнить цикл для каждого цикла, а оператор if не подойдет. Мне нужно сделать действие асинхронным. Что-то вроде await fetch и т. Д. Не знаю, как это сделать в методе действия.

Я тоже получаю эту ошибку, и я предполагаю, что если я сделаю свое действие, getPosts async, то он получит эту ошибку.

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 голосов
/ 11 января 2019

Выполнение асинхронного действия с избыточностью не решит вашу проблему, а приведет к зависанию интерфейса. Вместо этого не используйте componentWillMount, а вызывайте getPosts в componentDidMount и используйте componentDidUpdate для обновления состояния. Предполагая, что ваш компонент подключен к хранилищу резервов, вы можете сделать следующее:

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);

Обратите внимание, что вы обновляете состояние реагирующего компонента с помощью setState , а не напрямую.

Также обратите внимание, что вы должны обернуть this.setState в условие в componentDidUpdate; иначе вы создадите бесконечный цикл. Подробнее здесь .

Вышеописанное работает, потому что компонент, подключенный к редуксу, будет перерисовываться всякий раз, когда изменяются реквизиты, которые вы передаете ему, что вызывает componentDidUpdate.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...