git: https://github.com/techybolek/DUP-TESTCASE
Этот пример задуман как тестовый пример, демонстрирующий дублирующийся хук для следующего сценария:
Вывод консоли:
page.tsx:25 Text response received: Response for query:sample query 1
page.tsx:31 Invoke updateCurrentConversation with response: Response for query:sample query 1
page.tsx:31 Invoke updateCurrentConversation with response: Response for query:sample query 1
Источник:
"use client"
import React, { useState, useRef } from 'react';
export default function MyApp() {
const [input, setInput] = useState('');
const [conversations, setConversations] = useState([]);
const messagesEndRef = useRef(null);
const handleSubmit = async (event) => {
event.preventDefault();
const startTime = new Date()
const newConversation = { query: input, startTime, responses: [] }
setConversations((prevConversations) => [...prevConversations, newConversation]);
try {
await invokeServerAPI(input, textHandler)
}
finally {
setInput('');
}
function textHandler(text) {
console.info("Text response received:", text)
updateCurrentConversation(text)
}
function updateCurrentConversation(data) {
setConversations((prevConversations) => {
console.info('Invoke updateCurrentConversation with response: ', data)
const index = prevConversations.findIndex(conversation => conversation.startTime === startTime);
const _newConversation = { ...prevConversations[index] };
_newConversation.responses.push({ data })
return [
...prevConversations.slice(0, index),
_newConversation,
...prevConversations.slice(index + 1)
];
});
}
};
return (
<div className = "flex flex-col h-screen">
<div className = "overflow-y-scroll flex-grow p-2">
{conversations.map((conversation, index) => (
<div key = {index} className = "mb-5">
<p>{conversation.startTime.toLocaleString()} <span className = "font-bold">{conversation.query})</span>:</p>
{conversation.responses
.map((response, respIndex) => {
return (
<div key = {index * 1000 + respIndex} className = "mb-2">
<pre className = "mt-2 bg-gray-100 p-2 rounded border border-gray-300 whitespace-pre-wrap">{response.data}</pre>
</div>
);
})
}
</div>
))}
<div ref = {messagesEndRef} />
</div>
<div className = "p-2 bg-gray-800 text-white">
<form onSubmit = {handleSubmit}>
<div className = "flex justify-start space-x-4">
<div className = "flex flex-col" style = {{ marginRight: '1rem', marginLeft: '0' }}>
<label className = "text-black" style = {{ color: 'black', display: 'block', width: 'auto' }}>Query:</label>
<input
type = "text"
value = {input}
onChange = {(e) => setInput(e.target.value)}
className = "p-2 bg-gray-300 text-black rounded-md"
style = {{ fontSize: '18px', color: 'black', height: '40px', width: '500px' }}
placeholder = "Enter your query here"
/>
</div>
<div className = "flex flex-col items-start">
<label style = {{ color: 'black', display: 'block', width: 'auto' }}> </label>
<button type = "submit" className = "bg-green-500 text-white rounded-md hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50"
style = {{ paddingLeft: '1rem', paddingRight: '1rem', height: '40px', marginLeft: '0' }}>Send</button>
</div>
</div>
</form>
</div>
</div >
);
}
export function invokeServerAPI(theQuery, theHandler) {
return new Promise((resolve, reject) => {
//simulate just one response
theHandler('Response for query:' + theQuery)
resolve(null);
})
}
Когда вы вызываете setState, он запускает повторный рендеринг, если значение было изменено.
@zerkms Я не использую строгий режим.
Ну что ж. оказалось, что строгий режим включен неявно, потому что это приложение nextjs. Отключите его, и теперь setState вызывается только один раз.
Зачем вам отключать инструменты разработки, которые помогают писать правильный код? Оставьте его включенным и попытайтесь понять, что он включен по умолчанию по какой-то причине (т. е. чтобы помочь вам писать лучший код) и включен только в непроизводственных сборках.
@DrewReese Вероятно, ты прав. Однако я в растерянности, что еще делать.
Что вы имеете в виду, кроме отключения StrictMode, которое я не рекомендую, единственный вариант — оставить его включенным и воспользоваться преимуществами. Как я уже сказал, если оставить эту опцию включенной, вы сможете писать более качественный код. На мой взгляд, это проще простого. Когда срабатывает дымовая сигнализация, вы не устраняете ее, вынимая батарею, чтобы выключить сигнализацию, а находите источник дыма и тушите его.
@DrewReese Если я позволю ему вызываться дважды, это сломает мое приложение, потому что ответы добавляются дважды. Я пробовал фильтровать, подсчитывать звонки и многое другое, но мне не удалось заставить это работать. Возможно, я что-то упускаю. Я смиренно признаю, что не понимаю причину двойного вызова setState и не знаю, как с этим справиться. Под обработкой я имею в виду, в моем конкретном случае, как предотвратить двойное добавление элементов. Если есть какое-либо руководство, как обрабатывать двойной вызов setState, я был бы признателен.





import React from 'react';
function ExampleApplication() {
return (
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Footer />
</div>
);
}
Если в файле index.js есть React.StrictMode, удалите его.
Я не использую строгий режим.
ваша игровая площадка stackblitz не работает, не могли бы вы это исправить?
Извините, пришлось удалить это из поста.
setState вызывается дважды в строгом режиме в React. В Next JS, чтобы отключить строгий режим, отредактируйте файл next.config.mjs, как показано ниже, и строгий режим будет отключен.
Вы также можете ознакомиться с документацией actStrictMode.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
};
export default nextConfig;
Есть ли шанс, что вы используете реакцию
StrictMode?