Pull to refresh

Comments 26

PinnedPinned comments

Прошу прощения, я правильно понял, что в последнем листинге кода - ошибка, перепутаны местами аргументы вложенного коллбэка? вместо user => callback => нужно callback => user =>

Или другими словами: разделяйте логику и отрисовку.

да, так лучше будет в заголовке, спасибо )

разделяйте логику и отрисовку

Это точно не про React.

Прошу прощения, выскажу сугубо свое мнение. Это как-то слишком коротко и просто для статьи. И как заметили выше - если обобщить, то это просто разделение логики и верстки, что должно быть аксиомой и не является открытием. Более того, эта функция с проходом по массиву, подсчётами, псевдо проходами по массиву внутри прохода (to lowercase, includes) будет исполнятся на каждый rerender, хотя в этой трате ресурсов нет необходимости, ведь вы сказали, что данные просто пришли с бэка. Значит после того как они пришли с бэка нужно просто один раз пройтись и сформировать нужную структуру и отдать в этот компонент как пропс, более того убрав абсолютно ненужное тут усложнение в виде карирования. И если это все вынести в отдельный хук или файл например с названием utils/visualLogic, то код станет чище, понятнее для других разработчиков и более производительный.

а что еще можно рассказать про эту простую и неглубокую тему ?

С бэка приходит строка , фронт ее преобразует в элементы dom. Бэк стоит строку json, xml или html. Бэку по затратам по-барабану , что строить. Фронту же есть разница с чем работать, с json, xml или html. Для фронта самое оптимальное - html вставка в dom одна команда. Xcc? кто-то пробовал это сделать в современных браузерах? Результат приятно удивит. Да и есть уже инструменты... Теневой дом - та же работа по преобразованию входной строки в строку html . Бэки не умеют строить html? Пусть фронтеры дадут шаблон, чтоб его бэки заполнили. Или нафиг такие бэки. Серверный рендеринг? Хорошая идея загрузить сервер бесполезной работой по преобразованию из одной строки в другую..

да при таком подходе вообще от фронта отказаться можно, бэк умеет рендерить весь хтмл сам

А теперь представь мультиязычность, плюрализацию и склонения. Эта вся свалка должна быть на бэке? А если тебе нужно в разных случаях разные форматы рисовать? Прям например на юзера. В одном случае ФиО, в другом фамилию и инициалы, в третьем имя и дату последнего посещения.

это даже концептуально звучит неправильно. Все что связано с отображением должно строиться на уровне клиента.

Жсон юзают не потому что он быстрее хтмл, а потому что он удобнее, если говорить про спа. Во первых как минимум на этот кусок хтмла надо навесить события, надо будет вытянуть данные для того чтобы их можно было отредачить, чтобы их можно было куда засейвить, и прочее. И собсна разбираться как потом из хтмла вытянуть данные ради "оптимизации" не хочется. Плюс концепция реактивности(ну тут скорее даже не реактивности а стейтов будет нам мешать). Ну а как максимум апи нужно ещё и для других сред, например для мобильной аппы. Можно конечно дублировать генерацию хтмл и жсон, но мы опять же перетягиваем огромное количество лишнего кода на бэк

Во первых как минимум на этот кусок хтмла надо навесить события, /

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

Все что связано с отображением должно строиться на уровне клиента.

в итоге имеем веб вариант 1с - пока загрузится, пока отобразит - в Китае бамбука не хватит, чтоб курить в это время....

И собсна разбираться как потом из хтмла вытянуть данные ради "оптимизации" не хочется

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

это проблемы? ну это уже слишком.... элементарные вещи надо видеть сразу.

ребята! ну это не стоит и выеденного яйца.....

ели есть вопросы как решить эти ваши "проблемы" - пишите в "диалоги". ну или тут ждите сутки :)

Не могу понять, почему строки с ФИО остаются в нижнем регистре. Я понимаю, что условный пользователь может ввести все как зря, но почему потом имя не приводится к первой заглавной? Что делать с корнер-кейсами типа семитских или грузинских отчеств? Не имеет ли смысла запихнуть какую-то проверну не на этапе рендера, а на этапе ввода, и затем уже хранить правильные ФИО в базе, а тут не приводить их к нижнему регистру.

Во втором примере можно поправить опечатку retrun => return

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

  const processedUsers = useMemo(() => preprocessUserData(users), [users]);

  return (
    <ul className="list">
      {processedUsers.map((user) => (
        <UserListItem key={user.id} user={user} />
      ))}
    </ul>
  );

А почему вы не использовали паттерн RenderProps ?
C его помощью можно гибко резделять логику и отображение. И отдельно тестировать доменную и тестировать логику будет намного проще.

type User = {
  name: string;
  // другие пропсы
};
// Чистая функция, можно протестировать
function composeUser<T extends User>(
  user: T,
): { fullName: string; date: string; statusColor: string } {
    const fullName = `
    ${user.name.name}
    ${user.name.patronomic ? user.name.patronomic : ''}
    ${user.name.surname}
  `.toLowerCase();
    const date = user.regestrationDate ? moment(user.regestrationDate) : null;
    const statusColor = ["В сети", "Не в сети"].includes(user.status.description)
      ? STATUS_COLORS[user.status.value]
      : "neutral";

  return { fullName, date, statusColor };
}

const Users: FC<{ data: User[]; renderFn: (user: User) => void }> = ({
  data,
  renderFn,
}) => {
  return <>{data.map(renderFn)}</>;
};

const App: FC = () => {
  /// откуда то получаем users

  return (
      <ul className="list">
        <Users
          data={[{ name: "username1" }, { name: "username2" }]}
          renderFn={(c) => {
            const { fullName, date } = composeUser(c);
            return (
              <>
                <li>{fullName}</li>
                <li>{date}</li>
              </>
            );
          }}
        />
    </ul>
  )
}

proxyUserVars

Это пример, как максимально усложнить код, нагромождая ФП на ровном месте.

Всё то же самое, только чуть по-другому (+фрагмент, забытый незаслуженно):

Код
const getUserVars = (user) => ({
  fullName: `
    {user.name.name} 
    {user.name.patronomic ? user.name.patronomic : ''} 
    {user.name.surname}
  `.toLowerCase(),
  
  date: user.regestrationDate 
    ? moment(user.regestrationDate)
    : null,
  
  statusColor: ['В сети', 'Не в сети'].includes(
    user.status.description,
  )
    ? STATUS_COLORS[user.status.value]
    : 'neutral',
});

return (
  <ul className="list">
    {users.map((user) => {
      const {fullName, date, statusColor} = getUserVars(user);
  
      return (
        <Fragment key={user.id}>
          <li>{fullName}</li>
          <li>{date}</li>
          <li className={statusColor}>{user.status.description}</li>
        </Fragment>
      );
    ))}
  </ul>
);

Вообще, поддержу мысль @edtech, что правильнее делать отдельный UserListItem . А если список может меняться, то React.memo для ЛистАйтема будет совсем не лишним.

да в принципе можно и так, как вы написали, тоже хороший вариант ) я предложил вариант для тех, кто хочет упороться и съэкономить эти несчастные 4 строки и поупражняться с прокси. но в целом да, ваш вариант тоже хороший

{users.map(user => <User user={user}/>)}

В самом компоненте User будет разметка одного айтема, а логику вынесем в хук useUser.

Особого смысла делать это через коллбэк нет имхо, это может быть просто функция-конвертер, которую мы и так и так в компоненте используем, но будет легче дебажить и вообще разобраться, что и откуда пришло

Выносим список в <UsersList />, по-необходимости мемим преобразования внутри и успешно рисуем вью. Как было выше замечено плохая практика вызывать в ретурне на каждый рендер довольно сложную функцию. Декомпозиция вроде и была сделана, но при этом код по-прежнему афектит перформанс

// list.tsx
export const List = ({ className, children }): JSX.Element => (
  <ul className={ className }>
    { children }
  </ul>
);

// list-item.tsx
export const ListItem = ({ user }): JSX.Element => (
  <li className={ className }>
    { children }
  </li>
);

// fullname.tsx
export const FullName = ({ surname, name, patronymic }): JSX.Element => {
  const fullName = `
    {name} 
    {patronymic ?? ''} 
    {surname}
  `.toLowerCase();
  
  return (
    <span>
      { fullName }
    </span>
  );
};

// date.tsx
export const Date = ({ date }): JSX.Element | null => {
  if (!date) {
    return null;
  }
  
  return (
    <span>
      { moment(date) }
    </span>
  );
};

// status.tsx
import styles from './status.modules';

enum ColorByStatus = {
  'В сети': styles.statusOnline, 
  'Не в сети': styles.statusOffline,
};

export const Status = ({ description }): JSX.Element | null => (
  <span className={ ColorByStatus[description] ?? styles.statusDefault }>
    { description }
  </span>
);

// user-list.tsx
export const UserList = (users): JSX.Elemet => {
  return (
    <List className="user-list">
      {
        users.map(({ surname, name, patronymic, regestrationDate, status }) => (
          <FullName
            surname={ surname }
            name={ name }
            patronymic={ patronymic }
          />
          <Date date={ regestrationDate } />
          <Status description={ status.description } />
        )) 
      }
    </List>
  );  
};







Разве не best practices вынести элемент списка отдельным компонентом, в который передавать юзера и внутри него делать всю логику?

Прошу прощения, я правильно понял, что в последнем листинге кода - ошибка, перепутаны местами аргументы вложенного коллбэка? вместо user => callback => нужно callback => user =>

да, вы абсолютно правы - была ошибка!

Разве при таком вызове функции внутри map аргументы не поменяются местами?

users.map(proxyUserVars(callback))

Тоесть колбек зайдет при первом вызове функции, а элемент массива при втором. Тогда уж можно явно послать элемент вперед

users.map(user => proxyUserVars(user)(callback))

Ну или поменять порядок аргументов в прокси функции

А подготовить данные в ДТО не судьба ? Либо вызвать функцию на полученный респонс, либо уже в стейте, куда вы это складываете. Для чего эти преобразования тащить в UI?

Sign up to leave a comment.

Articles