2
votes

La fonction de tri React / Redux ne met pas à jour l'état

J'ai une application React / Redux simple qui affiche une liste de voitures basée sur mon API Rails.

J'essaie d'ajouter une fonction de tri qui classe les voitures par ordre alphabétique par leur nom.

Alors que ma variable orgArray est en fait classée par ordre alphabétique lorsque je console.log, mon outil de développement Redux dit que les états sont égaux après avoir cliqué sur le bouton de tri - donc mon interface utilisateur n'est pas mise à jour .

Voici mon code:

export default (state = {cars: []}, action) => {
switch(action.type) {
    case 'GET_CARS_SUCCESS':

    return Object.assign({}, state, {cars: action.payload})

    case 'CREATE_CAR_SUCCESS':

    return Object.assign({}, state, {cars: action.payload})

    case 'REMOVE_CAR;':
    return state.filter(car => car.id !== action.id)

    case 'SORT_CARS;':
    return Object.assign({}, state, { cars: action.payload})

    default:
        return state;
  }
}

J'aurais deviné que mapStateToProps ou en ajoutant le sortedCars: [] dans mon setState initial aurait fonctionné.

Essentiellement, mes accessoires sont en cours de mise à jour, mais j'ai également besoin que mon état soit mis à jour - bien que je ne le sois pas sûr de ce que je manque.

UPDATED:

Voici mon créateur d'action et mon action Async si cela aide:

const sortCars = cars => {
 return {
    type: 'SORT_CARS',
    cars
  }
}

// Async Actions

export const sortCar = (cars) => {
  console.log(cars, 'cars object');
  return dispatch => {
    dispatch(sortCars(cars))
   }
}

MISE À JOUR:

Voici également le réducteur:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import CarCard from '../components/CarCard';
import CarForm from './CarForm';
import './Cars.css';
import { getCars } from '../actions/cars';
import { sortCar } from '../actions/cars';

Component.defaultProps = {
  cars: { cars: [] }
}

class Cars extends Component {
  constructor(props) {
    super(props)
      this.state = {
        cars: [],
        sortedCars: []
      };
  }

sortAlphabetically = () => {
    console.log("sort button clicked")
    const newArray = [].concat(this.props.cars.cars)
    const orgArray = newArray.sort(function (a,b) {
      var nameA = a.name.toUpperCase();
      var nameB = b.name.toUpperCase();
          if (nameA < nameB) {
            return -1;
          } else if (nameA > nameB) {
            return 1;
          } 
            return 0;
          }, () => this.setState({ cars: orgArray }))  
            console.log(orgArray)
            this.props.sortCar(orgArray);
          }

    componentDidMount() {
        this.props.getCars()
        this.setState({cars: this.props.cars})
        }


    render() {
        return (
        <div className="CarsContainer">
    <h3>Cars Container</h3> 
        <button onClick={this.sortAlphabetically}>Sort</button>
        {this.props.cars.cars && this.props.cars.cars.map(car => <CarCard key={car.id} car={car} />)}  
        {/* {this.state.cars.cars && this.state.cars.cars.map(car => <CarCard key={car.id} car={car} />)}           */}
        <CarForm />
    </div>
    );
  }
}

 const mapStateToProps = (state) => {
   return ({
    cars: state.cars
  })
}

const mapDispatchToProps = (dispatch) => {
  return {
    sortCar: (cars) => dispatch(sortCar(cars)),
    getCars: (cars) => dispatch(getCars(cars))
  }
}

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


8 commentaires

Pouvez-vous trier le tableau dans votre résolveur? Il semble inutile de passer le tableau deux fois, une fois non trié et une fois trié.


Désolé, mais je ne suis pas sûr de ce que vous entendez par résolveur


Désolé, le réducteur


Attendez, votre code me déroute. Dans la fonction de tri, quelle est cette parenthèse après return 0; ?


Le crochet fermant après renvoie 0; devrait fermer la ligne newArray.sort (function


@Tom suggérez-vous que je mette la fonction de tri dans le réducteur? Ou ajoutez-y simplement setState: orgArray ?


@James ouais, je m'assurerais que les données qui vont à votre vue sont triées, nettoyées et prêtes à être utilisées.


Je suis toujours confus par l'indentation ... il semble que vous passiez () => this.setState ({cars: orgArray}) comme deuxième paramètre à la fonction de tri, mais tri ne prend qu'un seul paramètre?


4 Réponses :


0
votes

Le tri n'a qu'un seul argument (la fonction de tri). Et dans votre méthode sortAlphabetically, setState (placé comme deuxième argument de tri) n'est pas appelé. La méthode devrait être quelque chose comme ça (je ne l'ai pas testé)

sortAlphabetically = () => {
    console.log("sort button clicked")
    const newArray = this.props.cars.cars.slice();
    const orgArray = newArray.sort(function (a, b) {
        var nameA = a.name.toUpperCase();
        var nameB = b.name.toUpperCase();
        if (nameA < nameB) {
            return -1;
        } else if (nameA > nameB) {
            return 1;
        }
        return 0;
    });
    this.setState({ cars: orgArray }, () => {
        console.log(orgArray);
        this.props.sortCar(orgArray);
    });
}


1 commentaires

Malheureusement, cela ne met pas non plus à jour mon interface utilisateur. L'État reste égal



1
votes

Premièrement, je pense que vous n'avez pas besoin d'état ici, vous pouvez simplement mettre à jour le magasin et montrer les voitures à partir d'accessoires.

De plus, je pense que le problème ici est que vous transmettez orgArray à this.props.sortCar (orgArray) qui est un tableau au lieu d'un objet avec la clé et les valeurs "cars" à l'intérieur.

En dehors de cela, l'appel setState est déclaré comme une fonction anonyme au lieu d'être réellement exécuté après le tri.

Assurez-vous de mettre votre code en retrait.


10 commentaires

Bonne réflexion, je pense que cela contribuera à la solution, mais mon interface utilisateur ne se met pas à jour.


@James J'ai mis à jour mon commentaire, vérifiez la partie sur le passage du tableau au lieu de l'objet avec la clé "cars", assurez-vous que le réducteur fait ce qu'il est censé faire.


Merci, j'apprécie votre aide!


orgArray est un tableau d'objets, alors peut-être que je devrais le mapper quelque part? Je ne suis pas sûr des prochaines étapes à suivre


Je pense que vous devriez passer un objet ici this.props.sortCar ({cars: orgArray}) mais je ne peux pas être sûr sans voir le réducteur et le modèle / état / magasin initial, ce serait être utile de les inclure dans votre question.


Ajout du réducteur et des actions / créateurs.


Vous distribuez une action de type SORT_CAR mais dans le réducteur, vous attendez une action de type SORT_CARS; (vous devez supprimer ce point-virgule du nom), cela devrait être un problème aussi vous devriez passer un objet this.props.sortCar ({cars: orgArray}) comme je l'ai suggéré précédemment.


Correction du 'S'. Où devrais-je exactement passer cet objet? Dans le envoi de retour de mon action asynchrone?


Vous devez supprimer le point-virgule de l'action de réduction attendue case 'SORT_CARS' pas case 'SORT_CARS;' , et vous devez simplement appeler this.props.sortCar ({ cars: orgArray}) au lieu de this.props.sortCar (orgArray)


@James Marqueriez-vous cette réponse comme la bonne si vous pensez qu'elle a aidé à résoudre votre problème, merci :)



0
votes

Vous avez une solution qui utilise simplement l'état local au lieu du magasin Redux (pas idéal, mais remplit cette fonctionnalité).

Ajout de async à mon componentDidMount le long avec le wait attend de mettre à jour les données jusqu'à ce que l'état local ait changé, ce qui se reflète ensuite dans mon interface utilisateur.

Merci à tous pour votre aide.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import CarCard from '../components/CarCard';
import CarForm from './CarForm';
import './Cars.css';
import { getCars } from '../actions/cars';
import { sortCar } from '../actions/cars';

Component.defaultProps = {
  cars: { cars: [] }
}

class Cars extends Component {
  constructor(props) {
  super(props)
  this.state = {
    cars: [],
    sortedCars: []
  };
}

sortAlphabetically = () => {
    console.log("sort button clicked")
    const newArray = [].concat(this.props.cars.cars)
    const orgArray = newArray.sort(function (a,b) {
          var nameA = a.name.toUpperCase();
          var nameB = b.name.toUpperCase();
              if (nameA < nameB) {
                return -1;
              } else if (nameA > nameB) {
                return 1;
              } 
               return 0;
              })  
            console.log(orgArray)
            this.props.sortCar(orgArray);
            this.setState({ cars: {cars: orgArray} })
}

async componentDidMount() {
    await this.props.getCars()
    this.setState({cars: this.props.cars})
}

render() {
    return (
    <div className="CarsContainer">
        <h3>Cars Container</h3> 
        <button onClick={this.sortAlphabetically}>Sort</button>
        {this.state.cars.cars && this.state.cars.cars.map(car => <CarCard key={car.id} car={car} />)}          
        <CarForm />
    </div>
    );
  }
}

 const mapStateToProps = (state) => {
  return ({
    cars: state.cars
  })
}

const mapDispatchToProps = (dispatch) => {
  return {
    sortCar: (cars) => dispatch(sortCar(cars)),
    getCars: (cars) => dispatch(getCars(cars))
  }
}

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


0 commentaires

0
votes

Vous ne devez pas utiliser setState (). setState () ne modifie pas immédiatement this.state mais crée une transition d'état en attente. this.state après avoir appelé cette méthode peut potentiellement retourner la valeur existante. voici donc comment gérer le tri.

NOTEZ que par défaut, la méthode sort () trie les valeurs sous forme de chaînes par ordre alphabétique et croissant. donc vous n'avez pas besoin de définir de fonction de comparaison.

1-u peut définir une autre action et demander au réducteur de gérer le tri. la chose est ici, vous devez vous assurer que vous ne modifiez pas l'état, utilisez donc slice ()

const mapStateToProps = (state) => {
  return ({
    cars: state.cars.sort()
  })
}

2- vous pouvez définir une fonction et passer l'état en arguments et retourner l'état trié.

case 'SORT_CARS':
  return state.slice().sort()


0 commentaires