Олег
6 мая 2021 г., 14:14

Вернуть старый state после вызова setState в классовом компоненте

Есть классовый компонент, который при клике переворачивает карточку и добавляет стиль.
class Card extends React.Component { state = { done: false, } handleCardClick = () => { this.setState({ done: true, }); } render() { const {eng, rus} = this.props; const {done} = this.state; const cardClass = [s.card]; if (done) { cardClass.push(s.done); } return ( <div className={cardClass.join(' ')} onClick ={this.handleCardClick} > <div className={s.cardInner}> <div className={s.cardFront}> {eng} </div> <div className={s.cardBack}> {rus} </div> </div> </div> ); } } export default Card;

Мне нужно сделать так, чтобы после того как он открыл эту карточку, при клике опять по этой карточке он ее закрыл. Грубо говоря сбросил состояние к обычному state и вернул старое отображение. Я переписал обработчик таким образом:


handleCardClick = () => { if (this.state) { this.setState({ done: true, }); } else if (this.state === true){ return this.state } }
Но он открывать открывает, а закрыть не получается. Возможно вообще неправильно делаю, так как с контекстами классов, пока не очень хорошо разобрался. Вариант через хуки не предлагать, так как цель, как раз разобраться с классами и реалезовать - это через классовый компонент.
Работа со стейтем в функциональных и классовых компонентах немного отличается, в том плане, что в классовых ты работаешь с полным объектом, типа такого:
this.state = { done: false, title: "Some title", }
и можешь обращаться напрямую к его свойствам типа this.state.done

В функциональных же ты не напрямую со стейтом работаешь, а с его свойствами типа так:
const [done] = useState(false)
Во втором случае у тебя стейт как бы всегда отсутствует, есть только его значения (которые могут быть пустыми). В первом же случае (в классовых) у тебя стейт может не быть, а может и быть. То есть если ты раз задал this.setState({done: true}), то у тебя this.state уже есть всегда в течение жизни компонента. Таким образом у тебя логическая ошибка в этом условии:
if (this.state) { this.setState({ done: true, }); }
У тебя всегда есть this.state и получается, что всегда задается done: true. То есть надо было проверять
if (!this.state) { this.setState({ done: true, }); }
то есть проверять отсутствие стейта, а не его наличие. Ну, это в твоем случае. А вообще в таких случаях делают так:

handleCardClick = () => { this.setState({ done: !this.state?.done, }) }
То есть устанавливают инвертированное значение.




Можешь объяснить, как работает в данном случае тернарное выражение:


handleCardClick = () => { this.setState({ done: !this.state?.done, }) }

Не понимаю почему перед .done стоит точка.
Здесь нет тернарного выражения, то есть это не "если ? то : иначе". Это сокращенная форма записи на случай если нет объекта. То есть если ты запишешь так:
this.state.done, а объекта this.state нету, то ты получишь ошибку и твой код развалится. В классическом варианте ты должен записать this.state && this.state.done
Здесь же мы указываем знак вопроса, чтобы бабель за нас создал необходимые проверки. То есть мы пишем this.state?.done, указывая, что мы обращаемся к свойству done объекта this.state, но надо проверить если ли этот объект. Подробнее читай здесь: https://learn.javascript.ru/optional-chaining

В твоем случае при с классическим синтаксисом пришлось бы писать так:
handleCardClick = () => { this.setState({ done: !(this.state && this.state.done), }) }
Или так:
handleCardClick = () => { this.setState({ done: this.state && this.state.done ? false : true, }) }



Второй вариант не подгрузился. Я даже не знал, что есть такое - Опциональная цепочка '?.' думал странный, какой-то тернарник)
В первом варианте убрал скобки, со скобками не работает:
handleCardClick = () => { this.setState({ done: !(this.state ?? this.state.done), }) }
Получилось так:
handleCardClick = () => { this.setState({ done: !this.state.done ?? this.state.done, }) }
Мне кажется самая понятная конструкция получилась.

Через опциональную цепочку пока не догоняю почему он работает. А в третьем варианте слишком сложная конструкция получается логическое И плюс тернарник. В моем же варианте получается возьми или это или другое , другое или это. Правильно вообще написал и понимаю эту конструкцию?)
Нет, в первом варианте не в скобках дело, а в раскладке (на цифре 7 висят и & и ?). То есть должно быть не done: !(this.state ?? this.state.done), done: !(this.state && this.state.done)
>> Мне кажется самая понятная конструкция получилась.

Это самая неправильная конструкция :)
Точнее, она будет работать, но:
1. Если не будет объекта this.state, то будет ошибка (вспоминаем про ?.).
2. ?? this.state.done здесь вероятнее всего не имеет никакого смысла, то есть можно безболезненно удалить.
>> В моем же варианте получается возьми или это или другое , другое или это.
Твой вариант - это усложненный мой
handleCardClick = () => { this.setState({ done: !this.state?.done, }) }
К тому же, как я и сказал, если не будет объекта this.state, то будет ошибка. А это совсем не исключено, не всегда классовые компоненты объявляют с инициализацией стейта.

2. ?? this.state.done здесь вероятнее всего не имеет никакого смысла, то есть можно безболезненно удалить.

Да без него тоже работает.

done: !this.state?.done

Смысл работы этой конструкции вообще не понимаю, как и почему она работает(

И вообще теперь не понимаю почему и эта конструкция работает done: !this.state.done вроде нет никаких условий и все работает прям магия:)

Рука-лицо... Учи матчасть внимательней. Я зачем уроки сюда с FreeCodeCamp импортировал?
const newValue = !this.state?.done;
Устанавливаем новое значение. Восклицательный знак в начале - это отрицание Не. То есть если this.state?.done === true, то Не будет false. Иначе (то есть false, null, undefined, 0, '' и т.п.) будет приведено к логическому true.

?. здесь примеряется для предотвращения ошибки на случай если объект this.state отсутствует.
this.setState({ done: newValue, })
Устанавливаем значение в стейт.

А наш вариант
this.setState({ done: !this.state?.done, })
это просто сокращенная форма без использования дополнительной переменной newValue.


Продолжаем .... дальше .

Восклицательный знак в начале - это отрицание Не. - Здесь понятно.

То есть если this.state?.done === true, то Не будет false. - Если подразумевается, что ты говорил про восклицательный знак в предыдущем предложении, тогда получается !this.state?.done так как оно изначально было false значит он станет true.

Иначе (то есть false, null, undefined, 0, '' и т.п.) будет приведено к логическому true. (?. здесь примеряется для предотвращения ошибки на случай если объект this.state отсутствует. )

Здесь тоже понятно, что опциональная цепочка ?. проверяет есть ли объект this.state, если его нет он все равно продолжит выполнение. И так как после .? идет done он возьмет текущее состояние стейта объявленое по умолчанию false. (Это предположение здесь уже не понимаю, что реально на самом деле происходит.) И здесь еще кстати появился вопрос, даже если опциональная цепочка ?. проверяет наличие this.state там же стоит восклицательный знак отрицания, тогда получается он опять должен вернуть true.

В итоге получается, что если кликаем, когда state находится в состояние false он отправляет true, если state находится в состоянии false он отправляет true. Только я не понимаю, как вообще обработчик понимает в каком сейчас состоянии находится state, я думал так, что обработчик просто устонавливает и отправляет новое сотояние state, через setState, а получается он его еще и каким-то образом сравнивает его с текущим state.

Или я вообще опять все неправильно понимаю?
Ты каким-то удивительным образом умеешь понимать и не понимать одновременно...
Если тебе не понятна конструкция !this.state?.done, то перепиши ее в !(this.state?.done). Может так тебе будет понятноей? Ты путаешься с моментом когда именно применяется восклицательный знак, так как он стоит перед this... Но правильно воспринимать не (!this)(.state)(.done), а именно !(this.state?.done), потому что восклицательный знак воздействует на значение конечного элемента, а не на первый элемент, то есть на done, а не на this.
Еще ты можешь сделать так, чтобы было понятней:
// Получаем текущее значение const done: boolean | undefined = this.state?.done; // Записываем в новую переменную измененное значение const newDone: boolean = !done // Записываем стейт this.setState({ done: newDone, });
И можешь в консоли поиграться с такими конструкициями:
!null true !undefined true !'' true
Приведение типов. Отрицание пустого дает истину.

Ты каким-то удивительным образом умеешь понимать и не понимать одновременно... :)

У тебя тоже есть классная супер способность, делать сложные вещи, еще сложней для понимания:) Вот только пока не понимаю ты - это специально делаешь или ты на таком небесном уровне, что для тебя - это кажется простым:)

Ладно сделаем вид, что я понял, как работает эта конструкция !(this.state?.done). так или иначе насколько я понимаю она в разных случаях меняет значение на true или false.

Но я не понимаю, как она его меняет она использует текущий state для замены, сравнивает в каком он сотоянии находится. Вот этот вопрос, на который ты не ответил:

В итоге получается, что если кликаем, когда state находится в состояние false он отправляет true, если state находится в состоянии false он отправляет true. Только я не понимаю, как вообще обработчик понимает в каком сейчас состоянии находится state, я думал так, что обработчик просто устонавливает и отправляет новое сотояние state, через setState, а получается он его еще и каким-то образом сравнивает его с текущим state.

Мне кажется здесь основной затык. Я не понимаю setState при замене объекта отслеживает текущее состояние state, чтобы выполнять эти операции !(this.state?.done) или done: !this.state.done, чтобы эти конструкции работали, если я правильно понимаю дожен быть какой-то тригер, не могут же они сами по себе меняться. Клик в даном случае запускает функцию setState, но сами значение что-то же должно менять, как они принимают или то или другое значение в объекте, которым мы меняем state?


>> не могут же они сами по себе меняться

Они сами по себе и не меняются. Ты же вызываешь setState(...). И никакого сравнения здесь нет, здесь есть получение текущего значения и установка нового инвертированного.

В общем, ты не понимаешь как работают стейты в реакте. Иди уроки проходи начиная с этого.

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