web-dev-qa-db-fra.com

React le contexte avec des crochets empêche le rendu de nouveau

J'utilise le contexte React avec des crochets comme gestionnaire d'état pour mon application React. À chaque fois que la valeur change dans le magasin, tous les composants sont restitués).

Existe-t-il un moyen d'empêcher le composant React de restituer?

Configuration de la boutique:

import React, { useReducer } from "react";
import rootReducer from "./reducers/rootReducer";

export const ApiContext = React.createContext();

export const Provider = ({ children }) => {
  const [state, dispatch] = useReducer(rootReducer, {});

  return (
    <ApiContext.Provider value={{ ...state, dispatch }}>
      {children}
    </ApiContext.Provider>
  );
};

Un exemple de réducteur:

import * as types from "./../actionTypes";

const initialState = {
  fetchedBooks: null
};

const bookReducer = (state = initialState, action) => {
  switch (action.type) {
    case types.GET_BOOKS:
      return { ...state, fetchedBooks: action.payload };

    default:
      return state;
  }
};

export default bookReducer;

Réducteur de racines, qui peut combiner autant de réducteurs que possible:

import userReducer from "./userReducer";
import bookReducer from "./bookReducer";

const rootReducer = ({ users, books }, action) => ({
  users: userReducer(users, action),
  books: bookReducer(books, action)
});

Un exemple d'action:

import * as types from "../actionTypes";

export const getBooks = async dispatch => {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
    method: "GET"
  });

  const payload = await response.json();

  dispatch({
    type: types.GET_BOOKS,
    payload
  });
};
export default rootReducer;

Et voici le composant livre:

import React, { useContext, useEffect } from "react";
import { ApiContext } from "../../store/StoreProvider";
import { getBooks } from "../../store/actions/bookActions";

const Books = () => {
  const { dispatch, books } = useContext(ApiContext);
  const contextValue = useContext(ApiContext);

  useEffect(() => {
    setTimeout(() => {
      getBooks(dispatch);
    }, 1000);
  }, [dispatch]);

  console.log(contextValue);

  return (
    <ApiContext.Consumer>
      {value =>
        value.books ? (
          <div>
            {value.books &&
              value.books.fetchedBooks &&
              value.books.fetchedBooks.title}
          </div>
        ) : (
          <div>Loading...</div>
        )
      }
    </ApiContext.Consumer>
  );
};

export default Books;

Lorsque la valeur change dans le composant Livres, un autre utilisateur de mon composant effectue un nouveau rendu:

import React, { useContext, useEffect } from "react";
import { ApiContext } from "../../store/StoreProvider";
import { getUsers } from "../../store/actions/userActions";

const Users = () => {
  const { dispatch, users } = useContext(ApiContext);
  const contextValue = useContext(ApiContext);

  useEffect(() => {
    getUsers(true, dispatch);
  }, [dispatch]);

  console.log(contextValue, "Value from store");

  return <div>Users</div>;
};

export default Users;

Quelle est la meilleure façon d'optimiser les rendus de contexte? Merci d'avance!

5
lecham

J'ai essayé d'expliquer avec différents exemples l'espoir qui pourrait aider.

Étant donné que le contexte utilise l'identité de référence pour déterminer le moment du nouveau rendu, cela pourrait déclencher des rendus non intentionnels chez les consommateurs lorsque le parent d'un fournisseur effectue un nouveau rendu.

par exemple: le code ci-dessous restitue tous les consommateurs à chaque fois que le fournisseur effectue un nouveau rendu car un nouvel objet est toujours créé pour value

class App extends React.Component {
  render() {
   return (
      <Provider value={{something: 'something'}}>
        <Toolbar />
      </Provider>
    );
 }
}

Pour contourner ce problème, augmentez la valeur dans l'état du parent

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}
0
AviatorX

Cette solution est utilisée pour empêcher un composant de s'afficher dans React est appelé shouldComponentUpdate. Il s'agit d'une méthode de cycle de vie qui est disponible sur React composants de classe. Au lieu d'avoir Carré comme composant sans état fonctionnel comme auparavant:

const Square = ({ number }) => <Item>{number * number}</Item>;

Vous pouvez utiliser un composant de classe avec une méthode componentShouldUpdate:

class Square extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    ...
  }

  render() {
    return <Item>{this.props.number * this.props.number}</Item>;
  }
}

Comme vous pouvez le voir, la méthode de classe shouldComponentUpdate a accès aux accessoires et à l'état suivants avant d'exécuter le nouveau rendu d'un composant. C'est là que vous pouvez décider d'empêcher le nouveau rendu en renvoyant false à partir de cette méthode. Si vous retournez true, le composant effectue un nouveau rendu.

class Square extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.number === nextProps.number) {
      return false;
    } else {
      return true;
    }
  }

  render() {
    return <Item>{this.props.number * this.props.number}</Item>;
  }
}

Dans ce cas, si l'accessoire de numéro entrant n'a pas changé, le composant ne doit pas être mis à jour. Essayez-le vous-même en ajoutant à nouveau les journaux de la console à vos composants. Le composant Square ne doit pas être rendu à nouveau lorsque la perspective change. C'est un énorme gain de performances pour votre application React parce que tous vos composants enfants ne sont pas rendus à chaque rendu de leur composant parent. Enfin, c'est à vous d'empêcher le rendu d'un composant.

Comprendre cette méthode componentShouldUpdate vous aidera sûrement!

0
Tushar Sahu