#reactjs #redux #material-ui #redux-thunk
Вопрос:
Эти вещи с реакцией сводят меня с ума, я не помню, чтобы у меня давно были эти проблемы с компонентами класса, когда я работал над react native. Должно быть, я что-то здесь делаю не так, это одна из моих проблем, у меня есть еще один компонент, у которого также есть аналогичная проблема. Я новичок в новой системе крючков, даже читая о некоторых вещах, которые мне все еще не очень понятны.
У меня возникли некоторые проблемы с локальными состояниями и состояниями восстановления, из-за которых мой диалог мерцает. Каждый раз, когда родительский компонент устанавливал состояние журнала в этом цикле, диалоговое окно закрывалось и открывалось. (так что в этом случае 100 раз в зависимости от длины петли) Он перерисовывается каждый раз, когда обновляется состояние.
библиотеки реагируют/redux/redux-thunk/материал-пользовательский интерфейс
Пример Кода
function ToolbarModal(props){
const [progressLog, setProgressLog] = useState("");
const openProgressDialog = useSelector(getOpenProgressDialog);
const handleCloseLongProgressDialog = () => {
dispatch(handleOpenBatchRouteProgressDialog(false))
}
const handleSomeLogic = () => {
for(let i=0; i<0; i ){
//The real one is concated with prev message
setProgressLog("SAMPLE MESSAGE")
}
}
return (
<div>
<LongProgressDialog
open={openProgressDialog}
onClose={handleCloseLongProgressDialog}
onEntered={handleSomeLogic}
log={progressLog}
/>
</div>
)
}
Опора журнала передается дочернему компоненту с помощью диалогового компонента material-ui и просто отображается в текстовом поле.
function LongProgressDialog(props){
const {open, onClose, onEntered, log} = props
return (
<Dialog
open={open}
onClose={onClose}
onEntered={onEntered}
maxWidth='sm'
fullWidth
>
<DialogTitle>Long Progress</DialogTitle>
<DialogContent>
<TextField
multiline
fullWidth
rows={10}
rowsMax={10}
variant='outlined'
disabled
value={log}
/>
</DialogContent>
</Dialog>
)
}
Комментарии:
1. каждый раз , когда вы вызываете
setProgressLog
, вы изменяете переменную состояния и повторно визуализируете компонент. Именно так работает компонент react. Зачем вы используете переменную состояния, если не хотите, чтобы компонент повторно отображался?2. есть ли способ, которым я только повторно визуализирую текстовое поле внутри LongProgressDialog, вместо того, чтобы также повторно визуализировать родительский объект? Потому что я пытаюсь перенаправить сообщение из этой логической функции на медленное создание журнала.
3. нет, если текстовое поле находится внутри LongProgressDialog, то LongProgressDialog также должен быть перезапущен. Хотя оттуда вы могли бы использовать такие, как
React.memo
на некоторых дочерних компонентах внутри LongProgressDialog, чтобы остановить их ненужную перенастройку (в зависимости от того, какие реквизиты они получают)4. Когда вы меняете состояние, компоненты перерисовываются, но в вашем примере я думаю, что это не проблема. Если вы щелкаете диалоговым окном, это может быть проблемой в вашем компоненте LongProgressDialog. Я думаю, что вы тоже должны поделиться этим компонентом. Если переменная openProgressDialog изменяется при использовании функции setprograss, может возникнуть проблема с редуктором.
5. Вы должны добавить forwardRef в LongProgressDialog, вернуть что-то вроде функции changeText . Затем в ToolbarModal вызовите changeText в объекте useRef объекта LongProgressDialog
Ответ №1:
Я думаю, что вы хотите использовать useRef:
import { useRef } from 'react'
const Component = props => {
const logRef = useRef()
const handleSomeLogic = () => {
for(let i=0; i<100; i ){
logRef.current = "SAMPLE MESSAGE"
}
}
}
Затем передайте ссылку дочернему компоненту:
<LongProgressDialog
open={openProgressDialog}
onClose={handleCloseLongProgressDialog}
onEntered={handleSomeLogic}
log={logRef.current}
/>
И в вашем детском компоненте:
const LongProgressDialog = ({open, onClose, onEntered, log}) => {
...
return (
<p> Log value is: {log} </p>
)
}
Однако,
Обратите внимание, что, поскольку компонент не будет повторно отрисован, вы не увидите обновлений в дочернем элементе, если компонент не будет отрисован. Таким образом, вы должны решить, исходя из вашего случая, следует ли вам обновить состояние или обновить ссылку и затем выполнить повторную визуализацию. Вы можете проверить пример codesandbox здесь.
Комментарии:
1. Вероятно, это могло бы сработать, но, как вы упомянули, его необходимо уведомлять об обновлении пользовательского интерфейса при его изменении. Я хотел бы продолжать смотреть, смогу ли я заставить это работать с государствами.
2. Тогда нет другого способа, кроме как изменить состояние. React не может обновить значение и преобразовать обновленное значение в jsx без повторного рендеринга.
Ответ №2:
Каждый раз, когда ссылка на объект, который вы передаете как свойство, изменяется, FC (функциональный компонент) будет повторно отображаться. Таким образом, чтобы предотвратить это, передаваемые вами реквизиты должны иметь одну и ту же ссылку при каждом повторном рендеринге.
Позвольте мне объяснить более подробно:
Например, здесь:
const handleSomeLogic = () => {
//...
вы создаете новую функцию каждый раз, когда FC перерисовывается (что происходит при обновлении состояния).
Вы можете запомнить эту функцию с useCallback
помощью .
Функции, объекты (и массивы) меняют свое значение каждый раз, когда вы их создаете, поэтому:
const function1 = () => {}
const function2 = () => {}
function1 === function2 // false
const emptyObj = {}
const emptyObj2 = {}
emptyObj === emptyObj2 // false
// ... etc
Для того чтобы запомнить значения, вы должны использовать useMemo
крючок.
Итак, в качестве решения: оберните все свои функции useCallback
. Примечание: Я вижу, что вы передаете значение прогресса LongProgressDialog
компоненту, поэтому технически каждый раз, когда вы обновляете прогресс LongProgressDialog
, компонент будет повторно отображаться.
В дополнение к этому вы должны обернуть свой компонент React.memo
. Когда родитель повторно визуализирует, ребенок тоже будет повторно визуализировать. React.memo предотвращает это и будет повторно отображать компонент только в том случае, если один из его реквизитов был изменен.
Выяснение того, какая опора вызывает проблему
Чтобы выяснить, какая опора, которую вы передаете, вызывает повторную визуализацию, вы можете сравнить ссылки на переданные опоры в вашем компоненте, в вашем случае в LongProgressDialog
компоненте.
Так, например:
// create variables outside of the FC in which we will store the reference
let prop1Ref;
let prop2Ref;
const LongProgressDialog = ({ prop1, prop2 }) => {
// for each prop, use a effect which will be called when the prop's value/reference changes
useEffect(() => {
// compare the values by reference
if (prop1Ref !== prop1) {
// when prop ref has a value, the reference must have been changed:
if(prop1Ref != null) {
console.log("The reference of prop1 has changed. Memorize it properly");
}
prop1Ref = prop1;
}
}, [prop1])
}
Вы можете скопировать логику и применить ее ко всем вашим реквизитам.
Если вы не хотите проверять это вручную, есть удобные библиотеки, такие как https://github.com/welldone-software/why-did-you-render
Комментарии:
1. Я завернул все свои функции в useCallback, но они, похоже, не делали ничего другого. Дело в том, что даже если я не передам значение прогресса в свой компонент LongProgressDialog, как только я обновлю состояние с помощью setProgressLog, оно будет повторно отображаться, что приведет к закрытию и открытию диалогового окна.
2. Пожалуйста, отредактируйте свой вопрос и добавьте код того, что
getOpenProgressDialog
делает. Я подозреваю, что возвращаемое значение этого не запоминается. Вы можете дополнительно попробовать обернутьLongProgressDialog
React.memo
. Я отредактирую свой ответ, как вы можете продолжить расследование того, какая опора вызывает повторный перевод.3. getOpenProgressDialog возвращает только значение true/false из среза redux. Простой метод получения. const getOpeProgressDialog = состояние => состояние.ui.openProgressDialog
4. ладно, все в порядке. Затем проверьте мой обновленный ответ, похоже, вам нужно продолжить расследование того, что происходит
5. Я воспользовался вашим журналом. Открытый редуктор true/false изменяется только тогда, когда значение действительно изменилось. Так что этот редуктор работает правильно…
Ответ №3:
гу Минфэн ответил на него в своем комментарии. Просто используйте прямой реферат, исправил мою проблему, технически это то, что я искал все это время и не знал, что оно существует.
если кому-то это нужно, просто следуйте документу forward ref из документов react, и это очень просто.