Как стать автором
Обновить
1
0
Евгений Ткаченко @habrjeka

Пользователь

Отправить сообщение

Вы на 100% правы.

Юрист, преподаватель, который разобрался в PHP, React, Docker, GitHub, OSM, нейронках, парсингах ... Да Вы молодец!

Проблема: производительность - рендер Page и всех его дочерних компонентов каждый раз при вводе в input.

Причина: вызывается setState в handleChange при onChange input элемента

Решение: убираем состояние useState() для name;

без всяких useCallback

import React, { useState } from "react";

function Page() {
  const listOfCities = ["Beijing", "Tokyo", "Kinshasa", "Moscow", "Jakarta"];

  const [list, setList] = useState(listOfCities);
  let newItem = "";
  const handleClick = () => {
    setList([...list, newItem]);
    newItem = "";
  };

  const handleChange = (event) => {
    newItem = event.target.value;
  };

  const handleRemoveClick = (item) => {
    const filteredList = list.filter((listItem) => listItem !== item);

    setList(filteredList);
  };

  console.log("Page render");

  return (
    <div>
      <input type="text" onChange={handleChange} />
      <button onClick={handleClick}>Add</button>
      <CitiesList list={list} onRemoveClick={handleRemoveClick} />
    </div>
  );
}

export const CitiesList = ({ list, onRemoveClick }) => {
  console.log("List render");

  return list.map((item) => {
    return <City key={item} city={item} onRemoveClick={onRemoveClick} />;
  });
};

export const City = ({ city, onRemoveClick }) => {
  const handleCityClick = () => onRemoveClick(city);

  console.log("City render");

  return <div onClick={handleCityClick}>{city}</div>;
};

export default Page;

Пример в статье подобран неудачно ...

Агенты же тоже могут себе увеличить силу…
Чтобы уверенно разойтись по домам с шампанским надо как-то так:


class TheOne implements AbstractHuman{
  name;
  damage;

  constructor(name, damage) {
    this.name = name;
    this.damage = damage;
  }

  get name(): string {
    return this.name;
  }

  get damage(): number {
    return Number.POSITIVE_INFINITY;
  }
}

у машин нет шансов против Нео.


whoWin(): string {
    const result = this.agent.damage > this.human.damage
      ? this.agent.name
      : this.human.name;
    return result;
  }

условие всегда falseкак бы агент не рыпался ;)

Я полностью согласен — RecordStorage нужно покрыть интеграционным тестом, чтобы гарантировать его работу, как ожидается. Просто с тем кодом, где использовался private, вам нужна магия PHP, чтобы добраться до private, нам требуется знание черного ящика, тела функции, знания о её реализации...


Никогда не тестируйте защищённые/приватные методы
Основная причина: они влияют на то, как мы тестируем функции, определяя сигнатуру поведения: при таком-то условии, когда я ввожу А, то ожидаю получить Б. Приватные/защищённые методы не являются частью сигнатур функций.

А с кодом, который без private, мы проверяем результат RecordStorage::load($id) — подсовывая в constructor нужные нам состояния $cache и $storage, проверяем возвращаемый Record — никакой магии и тест будет читаться просто:


Дано RecordStorage и когда $cache и $storage такие-то, тогда RecordStorage::load($id) возвращает такой-то Record.


При private тестируется: будет ли второй раз при запросе к load отрабатывать кэш. А в третий, а в четвёртый — уверены?
Без private тестируется:


  • будет ли возвращён по id ожидаемый Record, который в cache, если он там имеется
  • будет ли возвращён по id ожидаемый Record, который в storage если в cache его нет.

Тут дело в том, что классу RecordStorage позволительно очень многое. По сути он создавался как сервис получения данных из хранилища.


В качестве хранилища у вас выступает doLoad() или $this->db->select('...') или т.д., по хорошему этот код должен быть в отдельном классе.


Сейчас за хранилище отвечает Сервис API получения данных из хранилища. Это и есть причина того, что есть потребность тестировать RecordStorage:load(). А если переписать код как указано ниже, то и тестировать нечего.


final class RecordStorage {

    /** Cache backend */
    private $cache;
    /** Cache backend */
    private $storage;

    public function load($id): Record
    {
        if ($record = $this->cache->get($id)) { // протестировано отдельным тестом
            return $record;
        }
        $record = $this->storage->dataById($id); // протестировано отдельным тестом
        $this->cache->set($id, $record); // протестировано отдельным тестом
        return $record;
    }
}

Пункт "Никогда не тестируйте защищённые/приватные методы", да и вообще рекомендации из статьи в целом хороши тем, что заставляют думать и писать "чистый код".

[удалено] не туда..

По хорошему:


  • $this->cache->get($id) нужно протестировать отдельным тестом.
  • $this->cache->set($id, $record); тоже нужно протестировать отдельным тестом.
  • doLoad() должен быть публичным, с вынесением в отдельный класс — тоже должен быть протестирован на граничные случаи.

Тогда RecordStorage:load() тестировать нет необходимости. RecordStorage превратиться сервис, который получает данные от протестированных компонентов и делегирует их в другие протестированные компоненты.


Так что для пункта 5. Никогда не тестируйте защищённые/приватные методы исключений не бывает ;)

Не ожидал, что в 2018 году придётся упоминать заголовок Pragma...

Предполагаю, что одна из причин, по которой он в топе — это отправка этого заголовка по умолчанию в PHP при старте сессии (Pragma: no-cache).
http://php.net/manual/ru/function.session-cache-limiter.php

Там рождается ещё один слоник.

Когда лень :/


var speed = 1000;
var clickEvent = new MouseEvent("click");
var elements = [].slice.call(document.getElementsByClassName("cell"));
elements.sort(function(a, b){return a.textContent - b.textContent;});

for(var i = 0; i < elements.length; i++) {  
    setTimeout(function(element){element.dispatchEvent(clickEvent);}, i*speed, elements[i]);
}

Первый вариант кстати тоже, он заменяется на:


'rules' => [
       ['class' => 'yii\rest\UrlRule', 'controller' => ['api/v1/users', /**...*/ ]],
]

Ну и демо данные набросать ещё можно, по аналогии с этим PR:


php yii fixture "User"

Мне вот очень зашло:


php yii migrate/create create_user --fields="username:string(255):notNull(),email:string():notNull()"
// проверяем, поправляем код миграции в файле
php yii migrate 
php yii gii/model --modelClass="User" --tableName="user" --ns="backend\models"
// поправляем, проверяем код модели в файле
php yii gii/crud --controllerClass="backend\controllers\UserController" --modelClass="backend\models\User"
// Теперь осталось код чуточку подправить и показать PM ;) 

Скелет CRUD приложения для вашей модельки готов, который вполне работает. При этом не надо было запускать браузер и "программировать мышкой".

Информация

В рейтинге
Не участвует
Откуда
Беларусь
Зарегистрирован
Активность