J'essaye de lire mes articles depuis ma base de données Firebase mais je n'arrive pas à le faire fonctionner.

Voici ma DB:

myDB-myid
  articles
    1
     body: "Lorem Ipsum Dolor si damet blablabla"
     createdAt: 12345
     subtitle: "ça promet beaucoup d'autres supers articles"
     title: "Mon premier article"
    2
     body: "Encore du Lorem Ipsum et bla et bla et bla..."
     createdAt: 34567
    subtitle: "Vous avez aimé le premier ? Le deuxième va vous..."
     title: "Et voici le deuxième article"

Mon composant qui devrait afficher la liste de tous les articles, ArticlePage:

import React from 'react';
import { connect } from 'react-redux';
import Article from './Article';
import { startSetArticles } from '../../actions/articles';

const ArticlesPage = props => (
      <div className="articles-wrapper">
        <div className="container">
        {console.log(props.articles)}
        {
          props.articles.length === 0 ? (
            <p>No article</p>
            ) : (
            props.articles.map(a => {
              return <Article key={a.id} {...a}/>
            })
            )
          }
        </div>
      </div>
      );

const mapDispatchToProps = dispatch => ({
  articles: () => dispatch(startSetArticles())
});

export default connect(undefined, mapDispatchToProps)(ArticlesPage);

Mon magasin:

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import authReducer from '../reducers/auth';
import articlesReducer from '../reducers/articles';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

export default () => {
  const store = createStore(
    combineReducers({
      auth: authReducer,
      articles: articlesReducer
    }),
    composeEnhancers(applyMiddleware(thunk))
  );

  return store;
};

Mes actions dans action/articles.js:

import database from '../firebase/firebase';

// SET_ARTICLES
export const setArticles = articles => ({
  type: 'SET_ARTICLES',
  articles
});

export const startSetArticles = () => {
  return dispatch => {
    return database.ref('articles').once('value').then(snapshot => {
      const articles = [];

      snapshot.forEach(a => {
        articles.push({
          id: a.key,
          ...a.val()
        });
      });
      dispatch(setArticles(articles));
    });
  };
};

Mon réducteur en reducers/articles.js:

const articlesReducerDefaultState = [];

export default (state = articlesReducerDefaultState, action) => {
  switch (action.type) {
      case 'SET_ARTICLES':
        return action.articles;
    default:
      return state;
  }
};

Mais je ne peux pas le faire fonctionner. Voici la sortie console du console.log(props.articles) du composant:

ƒ articles() {
      return dispatch((0, _articles.startSetArticles)());
    }

Une idée ? Il devrait rendre les deux messages mais il ne renvoie que la fonction elle-même. (Je reçois un 'no article' de l'opérateur ternaire)

1
Uj Corb 23 mai 2018 à 18:41

3 réponses

Meilleure réponse

articles est une fonction car c'est exactement ce que vous faites:

const mapDispatchToProps = dispatch => ({
  articles: () => dispatch(startSetArticles())
});

Vous mappez la distribution aux accessoires et vous l'avez mappée à un accessoire appelé articles qui est une fonction qui distribue une action. Pas de surprise ici. Mais je suppose que vous voulez également mapper l'état aux accessoires , ce que vous pouvez faire avec un premier argument de connexion:

const mapStateToProps = state => ({ articlesList: state.articles });
export default connect(mapStateToProps, mapDispatchToProps)(ArticlesPage);

Maintenant, si vous faites console.log(props), vous devriez obtenir un objet avec deux propriétés:

{
  articles, // a function from mapDispatchToProps
  articlesList // a function from mapStateToProps 
}

MODIFIER:

Cependant, vous devez toujours appeler votre fonction articles pour récupérer des données avec Firebase et remplir le magasin redux. La meilleure façon de démarrer des données asynchrones est d'utiliser le hook componentDidMount ():

class ArticlesPage extends React.Component {
  componentDidMount() {
    this.props.articles();  // use mapped function to dispatch redux action
  }

  render() {
    // render data with array from the store
    return (
      <div className="articles-wrapper">
        <div className="container">
          {this.props.articlesList.length === 0 ? (
            <p>No article</p>
          ) : (
            this.props.articlesList.map(a => {
              return <Article key={a.id} {...a} />;
            })
          )}
        </div>
      </div>
    );
  }
}
1
Tomasz Mularczyk 23 mai 2018 à 17:39

Vous devez utiliser le premier paramètre de connect pour obtenir l'état articles.

connect(state => ({ articles: state.articles }), ...)

Pour définir l'état articles, vous devez appeler startSetArticles quelque part comme componentDidMount ou dans le fichier où vous déclarez votre store (si vous souhaitez qu'il soit appelé lorsque l'application charges) avec store.dispatch(startSetArticles()).

0
riwu 23 mai 2018 à 15:45

Je pense que vous ne vous êtes connecté qu'au magasin, mais n'avez jamais appelé la fonction startSetArticles(). Essayez d'ajouter (cela fonctionnera avec votre mapDispatchToProps actuelle)

  componentDidMount() {
    this.props.articles()
  }

Cela devrait appeler la fonction, qui devrait mettre à jour votre boutique.

Et je recommanderais au lieu de:

const mapDispatchToProps = dispatch => ({
  articles: () => dispatch(startSetArticles())
});

Vous devez simplement faire ceci:

const mapDispatchToProps = dispatch => ({
  startSetArticles: () => dispatch(startSetArticles())
});

Ensuite, vous devrez le faire, ce qui est plus logique.

  componentDidMount() {
    this.props.startSetArticles()
  }

Je pense que votre confusion concerne l'utilisation de mapDispatchToProps. Lorsque vous mapDispatchToProps, il DOES NOT appelle cette fonction immédiatement. Vous devrez toujours appeler cette fonction quelque part dans le composant. Dans votre cas, componenetDidMount a du sens.

Et puis vous devez ajouter mapStateToProps, qui se connectera au magasin et récupérera les données.

Voici un exemple de travail:

export class Morphology extends React.Component {
  componentDidMount() {
    document.title = 'Eflows | Morphology';
    this.props.fetchGeoSites();
  }

  render() {
    return (
      <div>
        <div style={styles.banner} />
        <Layout geoSites={this.props.geoSites} />
      </div>
    );
  }
}

Morphology.propTypes = {
  fetchGeoSites: PropTypes.func,
  geoSites: PropTypes.array,
};

const mapStateToProps = state => {
  return {
    geoSites: state.geoSite.geoSites,
  };
};

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

J'utilise la même méthode, vous pouvez l'examiner plus en détail avec ce repo: https://github.com/leogoesger/eflow-client

1
leogoesger 23 mai 2018 à 17:52