Сохранять ли встроенные изменения стиля при отправке?

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

Вот демонстрация того, что я имею в виду.

введите описание изображения здесь

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

import React, { useEffect, useRef } from "react";
import { useDispatch, connect } from "react-redux";
import { addProfessionExperience } from "../../actions/index";

import "./Professions.sass";

const timers = [];
const progressWidths = [];

const mapStateToProps = (state, ownProps) => {
    const filterID = +ownProps.match.params.filter;
    const { professions, professionExperience } = state;

    return {
        professions: professions.find(item => item.id === filterID),
        professionExperience: professionExperience
    };
};

const produceResource = (dispatch, profession, sub, subRef) => {
    if(timers[sub.id]) return;

    /*
    * Begin the progress bar animation/width-change.
    */
    Object.assign(subRef.current[sub.id].style, {
        width: "100%",
        transitionDuration: `${sub.duration}s`
    });

    /*
    * Updates the progress text with the remaining time left until done.
    */
    let timeLeft = sub.duration;

    const timeLeftCountdown = _ => {
        timeLeft--;
        timeLeft > 0 ? setTimeout(timeLeftCountdown, 1000) : timeLeft = sub.duration;

        subRef.current[sub.id].parentElement.setAttribute("data-duration", timeLeft + "s");
    }

    setTimeout(timeLeftCountdown, 1000);

    /*
    * Dispatch the added experience from profession ID and sub-profession level.
    * We do not allow duplicate timers, only one can be run at a time.
    */
    const timer = setTimeout(() => {
        Object.assign(subRef.current[sub.id].style, {
            width: "0%",
            transitionDuration: "0.2s"
        });

        dispatch(addProfessionExperience({ id: profession.id, level: sub.level }));

        delete timers[sub.id];
    }, sub.duration * 1000);

    timers[sub.id] = timer;
};

const isSubUnlocked = (professionMaxExperience, subLevel, professionExperience) => {
    if(professionExperience <= 0 && subLevel > 1) return false;

    return professionExperience >= getExperienceThreshold(professionMaxExperience, subLevel);
};

const getExperienceThreshold = (professionMaxExperience, subLevel) => (((subLevel - 1) * 1) * (professionMaxExperience / 10) * subLevel);

const ConnectedList = ({ professions, professionExperience }) => {
    const currentExperience = professionExperience.find(item => item.profession === professions.id);
    const subRef = useRef([]);
    const dispatch = useDispatch();

    useEffect(() => {
        subRef.current = subRef.current.slice(0, professions.subProfessions.length);
    }, [professions.subProfessions]);

    return (
        <div>
            <div className="list">
                <ul>
                    {professions.subProfessions.map(el => {
                        const unlocked = isSubUnlocked(
                                            professions.maxExperience, 
                                            el.level, 
                                            (currentExperience ? currentExperience.amount : 0)
                                        );

                        const remainingExperience = getExperienceThreshold(professions.maxExperience, el.level) - (currentExperience ? currentExperience.amount : 0);

                        return (
                            <li 
                                key={Math.random()} 
                                style={{ "opacity": unlocked ? "1" : "0.5" }}
                                >
                                <div className="sprite">
                                    <img alt="" src={`/images/professions/${el.image}.png`} />
                                </div>

                                <div className="caption">{el.name}</div>

                                <div 
                                    className="progress-bar" 
                                    data-duration={unlocked ? `${el.duration}s` : `${remainingExperience} XP to Unlock`} 
                                    data-identifier={el.id}
                                >
                                    <span ref={r => subRef.current[el.id] = r} ></span>
                                </div>

                                <div className="footer">
                                    <button 
                                        className="btn" 
                                        onClick={() => unlocked ? produceResource(dispatch, professions, el, subRef) : false}
                                    >
                                        {unlocked ? 
                                                `Click` : 
                                                <i className="fa fa-lock"></i>
                                        }
                                    </button>
                                </div>
                            </li>
                        );
                    })}
                </ul>
            </div>
        </div>
    );
};

const List = connect(mapStateToProps)(ConnectedList);

export default List;

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

Всего 1 ответ


Одна из проблем заключается в том, что вы используете Math.random() для генерации ваших ключей. Ключи - это то, что использует виртуальный DOM, чтобы определить, является ли элемент «тем же», что и элемент предыдущего рендера. Используя случайный ключ, вы сообщаете виртуальному DOM, что вы хотите выпустить совершенно новый элемент DOM вместо повторного использования предыдущего, что означает, что новый не сохранит ни одного из побочных эффектов, которые вы поместили на оригинал. элемент. Читайте о сверке React для получения дополнительной информации об этом.

Попробуйте использовать ключи, которые логически представляют то, что вы визуализируете. В случае вашего кода el.id выглядит так, как будто он может быть уникальным идентификатором подпрофессии, которую вы отображаете. Используйте это для ключа вместо Math.random() .

Кроме того, ссылки сделают рассуждения о вашем коде действительно сложными. Вместо того, чтобы использовать refs для манипулирования вашим DOM, используйте манипуляции с состоянием и передачу пропуска, и позвольте React перерисовать ваши элементы с новыми атрибутами.


Есть идеи?

10000