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

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

  • message для получения сообщения и

  • bold который извлекает то, что выделено жирным шрифтом (с start а также end ключи к подстроке) о message передается как параметр

Я попытался оптимизировать взаимодействие с пользователем, всегда имея два доступных сообщения, так что, когда пользователь нажимает «далее», уже есть сообщение для отображения и нет времени ожидания. Эти currentMessage а также nextMessage.

Я использовал два перехватчика useState, так как обнаружил, что цикл внутри перехватчика useEffect при извлечении несколько сложен. Все еще хотел бы иметь возможность это сделать.

Более того, я использовал два useEffect. Первый просто запускается один раз, чтобы получить начальное сообщение, а другой запускается при отображении страницы, а затем каждый раз, когда пользователь нажимает «Далее». Когда пользователь нажимает кнопку «Далее», приложение меняет текущее сообщение на то, которое находилось в «очереди».

const MessageBox = () => {
  const [message, setCurrentMessage] = useState("");
  const [bold, setCurrentBold] = useState("");
  const [nextMessage, setNextMessage] = useState("");
  const [nextBold, setNextBold] = useState("");
  const [isGettingMessage, setIsGettingMessage] = useState(true);

  useEffect(() => {
    fetch(`https://myapi.com/message`)
      .then((res) => res.json())
      .then((res) => {
        if (res.data) {
          setCurrentMessage(res.data.message);
          getBold(res.data.message, setCurrentBold);
        } else {
          console.log("handle err");
        }
      });
  }, []);

  useEffect(() => {
    if (!isGettingMessage) return;
    fetch(`https://myapi.com/message`)
      .then((res) => res.json())
      .then((res) => {
        if (res.data) {
          setNextMessage(res.data.message);
          getBold(res.data.message, setNextBold);
        } else {
          console.log("handle err");
        }
      });
    setIsGettingMessage(false);
  }, [isGettingMessage]);

  const getBold = (message, boldSetter) => {
    if (!message) return;
    fetch(`https://myapi.com/bold`, {
      method: "POST",
      headers: { "Content-type": "application/json" },
      body: JSON.stringify({ content: message }),
    })
      .then((res) => res.json())
      .then((res) => {
        if (res?.data?.bold.length) {
          boldSetter(res.data.bold);
          return;
        } else if (res.error) {
          console.log("handle err");
        }
        boldSetter("");
      });
  };

  const formatMessageAndBold = () => {
    if (message && bold) {
      return boldMessage;
    } else if (message && !bold) {
      return <span>{message}</span>;
    }
  };

  const boldMessage = useMemo(
    () => (
      <p>
        <span>{message.substring(0, bold.start)}</span>
        <strong>{message.substring(bold.start, bold.end)}</strong>
        <span>{message.substring(bold.end)}</span>
      </p>
    ),
    [message, bold.start, bold.end]
  );

  const messageAndBold = formatMessageAndBold();

  const onClick = () => {
    if (isGettingMessage) return;
    setIsGettingMessage(true);
    setCurrentMessage(nextMessage);
    setCurrentBold(nextBold);
  };

  return (
    <div>
      {messageAndBold}
      <button isLoading={nextMessage === message || !message} onClick={onClick}>
        {message ? "Next Message" : "Loading..."}
      </button>
    </div>
  );
};

export default MessageBox;
```

1 ответ
1

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

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

Здесь я предлагаю поместить вашу логику в несколько разных файлов:

  • constants.js
  • helpers.js
  • hooks.js
  • MessageBox.js

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

  • useQueryFullMessage — обрабатывает получение сообщения + жирный шрифт и возврат результата
  • useFullMessage — использует useQueryFullMessage дважды, чтобы получить два полных сообщения и обрабатывает логику предварительной загрузки следующего полного сообщения.

Это возможная абстракция (среди многих), которую вы можете использовать.

Я тоже не верю в это useMemo в этой ситуации требуется … если вы не собираетесь загружать много сообщений. Реагирование по умолчанию уже довольно оптимизировано.

//constants.js
const API_MESSAGE_URL = 'https://myapi.com/message';
const API_BOLD_URL = 'https://myapi.com/bold';

//helpers.js
import { API_MESSAGE_URL, API_BOLD_URL } from './constants';

const getMessage = async () => {
  const res = await fetch(API_MESSAGE_URL);
  const {data} = await res.json() ?? {};
  return data?.message;
}

const getBold = async (message) => {

  const res = await fetch(API_BOLD_URL, {
    method: "POST",
    headers: { "Content-type": "application/json" },
    body: JSON.stringify({ content: message }),
  });
  const {data} = await res.json() ?? {};
  return data?.bold;
}

export const getFullMessage = async () => {
  const message = await getMessage();
  if(!message) {
    // handle error
    return {};
  }
  const bold = await getBold(message);
  if(!bold) {
    // handle error
  }
  return { 
    message,
    bold
  };
}

//hooks.js
import { getFullMessage } from './helpers';

const useQueryFullMessage = () => {
  const [fullMessaage, setFullMessage] = useState();
  const [loading, setLoading] = useState(true);
  const [preloadNextValue, togglePreloadNext] = useState(0);
  useEffect(async () => {
    if(!loading) {
      setLoading(true);
    }
    const fullMessage = await getFullMessage();
    // handle if bold not set
    setFullMessage(fullMessage);
    setLoading(false);
  }, [preloadNextValue]);

  return {
    fullMessage,
    loading,
    preloadNext: () => togglePreloadNext(preloadNextValue + 1),
    setFullMessage,
  }
}

export const useFullMessage = () => {
  const {
    fullMessage: currentFullMessage,
    loading: loadingCurrentFullMessage,
    setFullMessage: setCurrentFullMessage
  } = useQueryFullMessage();
  const {
    fullMessage: nextFullMessage,
    loading: loadingNextFullMessage,
    preloadNext
  } = useQueryFullMessage();

  const requestNextMessage = () => {
    if(loadingCurrentFullMessage || loadingNextFullMessage) {
      return;
    }
    setCurrentFullMessage(nextFullMessage);
    preloadNext();
  }

  return {
    currentFullMessage,
    loadingCurrentFullMessage,
    nextFullMessage,
    loadingNextFullMessage,
    requestNextMessage
  };
}

// MessageBox.js
import { useFullMesage } from './hooks';
const MessageBox = () => {
  const {
    currentFullMessage,
    loadingCurrentFullMessage,
    nextFullMessage,
    loadingNextFullMessage,
    requestNextMessage
  } = useFullMessage();

  const { message, bold } = fullMessage;
  // TODO: check if message and bold are set.

  const formatMessageAndBold = () => {
    if (message && bold) {
      return <p>
        <span>{message.substring(0, bold.start)}</span>
        <strong>{message.substring(bold.start, bold.end)}</strong>
        <span>{message.substring(bold.end)}</span>
      </p>;
    } else if (message && !bold) {
      return <span>{message}</span>;
    }
  };

  return (
    <div>
      {loadingCurrentFullMessage ? "Loading..." : formatMessageAndBold()}
      <button isLoading={loadingNextFullMessage} onClick={requestNextMessage}>
        {!loadingNextFullMessage ? "Next Message" : "Loading..."}
      </button>
    </div>
  );
}
```

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *