Реагируйте повторно, реагируйте на элемент, состояние элемента будет сброшено

#javascript #reactjs #typescript

Вопрос:

Я хочу использовать систему react make act, такую как система активности Android

следуя моему коду, ошибка в том, что я установил состояние actE k:»v», но последняя строка app.setState({act: actE}) вызывает состояние actE k: null, почему состояние actE сброшено?

     class Act extends PureComponent<any, { k: string }> {
        constructor(props: any, context: any) {
            super(props, context)
            this.state = {
                k: null
            }
        }

        render() {
            return this.state.k
        }
    }

    class Act2 extends Act {

    }

    class App extends PureComponent<any, { act: ReactElement }> {
        constructor(props: any, context: any) {
            super(props, context)
            this.state = {
                act: null
            }
        }

        render() {
            return this.state.act;
        }
    }

    var app: App

    ReactDOM.render(<App ref={e => app = e}/>, document.querySelector("#root"))
    var act: Act
    let actE = <Act ref={(e) => {
        if (e) {
            act = e
        }
    }}/>
    app.setState({act: actE})
    act.setState({k: "v"})

    var act2: Act2
    var act2E = <Act2 ref={(e) => {
        if (e) {
            act2 = e
        }
    }}/>
    app.setState({act: act2E})
    act2.setState({k: "v2"})

    app.setState({act: actE})
 

Комментарии:

1. Попробуйте добавить useEffect(() => () => console.log('unmounted'), []) к нему. Скорее всего, он размонтирован.

2. @windmaomao Я действительно пробовал что-то подобное некоторое время назад, но сдался по той же причине. Я предлагаю вам взглянуть на существующие библиотеки навигации и на то, как они хранят состояние. если вы узнаете, пожалуйста, дайте мне знать. На самом деле мне тоже очень интересно.

3. это кажется интересным чтением: blog.atulr.com/react-custom-renderer-1

4. Я думаю о создании отдельного контейнера реакций для каждого экрана. Это важно, так как крючки и другие вещи всегда должны быть в одном и том же порядке. И вы, скорее всего, не захотите отображать экраны, которые не отображаются.

Ответ №1:

Я действительно пропустил эту строчку поначалу. Поэтому я подумал, что вы управляете «скрытыми» компонентами.

   return this.state.act;
 

Но на самом деле вы пытаетесь смонтировать actE act2E , а затем вернуться к actE … хорошо . Ваш код, по сути, преобразуется в нечто подобное в обычном коде реакции.

   const actE = <Act ref={ref} />, act2E = <Act ref={ref2} />
  return flag ? actE : act2E
 

После actE того, как он размонтирован, что случилось с вещами внутри?

Проблема заключается в следующей строке.

     let actE = <Act ref={(e) => {
        if (e) {
            act = e
        }
    }}/>
 

Вы не можете использовать if здесь. Потому что, если вы это сделаете, вы пропустите размонтирование. act в приведенном выше коде предполагается null , что это произойдет после размонтирования. И React хочет убедиться, что после этого больше ничто из внутреннего не будет открыто.

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

Ответ №2:

Мое решение tmp заключается в том, чтобы кэшировать повторно используемое состояние элемента реакции и переменные экземпляра, при повторном использовании элемента реакции вручную установите кэш

ниже приведен мой код:

     class Act extends PureComponent<any, { k: string }> {
        constructor(props: any, context: any) {
            super(props, context)
            this.state = {k: null}
        }

        k2: string

        render() {
            return `${this.state.k} ${this.k2}`
        }
    }

    class Act2 extends Act {
    }


    class App extends PureComponent<any, { act: ReactElement }> {
        constructor(p: any) {
            super(p)
            this.state = {act: null}
        }

        render() {
            return this.state.act
        }
    }

    var parentKeys = Object.keys(new PureComponent({}))

    var app: App

    var act: Act
    /**
     * reactElement is replace will be unmounted, for ex, my following code act2E instead actE, actE will be unmounted, if I wanna reuse actE, I should cache it's 'state' and instance vars
     * @param act
     */
    var getCache = (act: any) => {
        var keyVal: any = {}
        var keys = Object.keys(act).filter((e) => {
            return !parentKeys.includes(e)
        })
        keys.forEach((key) => {
            if (key != "state" amp;amp; !key.match(/^_react/i)) { // should use special meth #setState to set, _react I don't need it
                keyVal[key] = act[key]
            }
        })
        return {keyVal: keyVal, state: act.state}
    }

    var setCache = (newAct: any, actCache: any) => {
        for (var [k, v] of Object.entries(actCache.keyVal)) {
            newAct[k] = v
        }

        act.setState(actCache.state)
        act.forceUpdate()
    }

    var actE = <Act ref={(e) => {
        act = e
    }}/>
    var act2: Act2
    var act2E = <Act2 ref={(e) => {
        act2 = e
    }}/>

    testReact(<App ref={e => app = e}/>)
    app.setState({act: actE})
    act.setState({k: "v"})
    act.k2 = "k2"
    var actCache = getCache(act)
    app.setState({act: act2E})
    act2.setState({k: "v2"})
    app.setState({act: actE})
    setCache(act, actCache)