2016-05-24 3 views
1

Через my previous question я узнал, что React сохраняет состояние для дочерних компонентов автоматически и поэтому its documentation says:Как сохранить состояние для «просто» скрытого компонента

Что Не должен Go в штате?

Реактивные компоненты: Построить их в render() на основе базовых опор и состояния.

Теперь мой вопрос заключается в том, как сделать то же самое для компонентов, которые мы только скрываем?

Рассмотрите дочерний компонент, который на итерации не будет показан, и в то же время мы хотели бы сохранить его состояние в будущем, когда оно будет возвращено!

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

Executable code

class BlackBox extends React.Component 
{ 
    constructor() { 
    super(); 
    this.state = { 
     checked: false, 
     counter: 0, 
    }; 
    } 

    increment =() => { 
    this.setState(Object.assign({}, this.state, { counter: this.state.counter+1 })); 
    }; 

    switch =() => { 
    this.setState(Object.assign({}, this.state, { checked: !this.state.checked })); 
    }; 

    isChecked() { 
     return this.state.checked; 
    } 

    render() { 
    return (
     <span onClick={this.increment} title={this.props.id} style={{ 
     fontSize: '24pt', 
     border: '1px solid black', 
     margin: 10, 
     padding: 10, 
     }}> 
     <input type="checkbox" onChange={this.switch} checked={this.state.checked} /> 
     {this.state.counter} 
     </span> 
    ); 
    } 
} 

class RedBox extends React.Component 
{ 
    constructor() { 
    super(); 
    this.state = { 
     checked: false, 
     counter: 0 
    }; 
    } 

    increment =() => { 
    this.setState(Object.assign({}, this.state, { counter: this.state.counter+1 })); 
    }; 

    switch =() => { 
    this.setState(Object.assign({}, this.state, { checked: !this.state.checked })); 
    }; 

    isChecked() { 
     return this.state.checked; 
    } 

    render() { 
    return (
     <span onClick={this.increment} title={this.props.id} style={{ 
     fontSize: '24pt', 
     border: '1px solid red', 
     margin: 10, 
     padding: 10, 
     }}> 
     <input type="checkbox" onChange={this.switch} checked={this.state.checked} /> 
     {this.state.counter} 
     </span> 
    ); 
    } 
} 

class Parent extends React.Component { 
    static blackCount = 0; 
    static redCount = 0; 
    state = { 
     childCmps: [], 
     showOnlyChecked: false, 
    }; 
    constructor(props, context) { 
     super(props, context); 
    } 

    addBlackBox =() => { 
     this.setState(Object.assign({}, this.state, { 
     childCmps: [...this.state.childCmps, { Component: BlackBox, id: "black" + (++Parent.blackCount) }], 
     })); 
    }; 

    addRedBox =() => { 
     this.setState(Object.assign({}, this.state, { 
     childCmps: [...this.state.childCmps, { Component: RedBox, id: "red" + (++Parent.redCount) }], 
     })); 
    }; 

    showHide =() => { 
     this.setState(Object.assign({}, this.state, { 
     showOnlyChecked: !this.state.showOnlyChecked, 
     })); 
    }; 

    render() { 
     let children = this.state.childCmps.map(child => <child.Component key={child.id} id={child.id} ref={child.id} />); 
     return (
     <div> 
      <button onClick={this.addBlackBox}>Add Black Box</button> 
      <button onClick={this.addRedBox}>Add Red Box</button> 
      <button onClick={this.showHide}>Show/Hide Unchecked</button> 
      <br /><br /> 
      {children.filter(box => !this.state.showOnlyChecked || this.refs[box.props.id].isChecked())} 
     </div> 
    ); 
    } 
} 

ReactDOM.render(
    <Parent />, 
    document.getElementById("root") 
); 

ответ

1

Короткий ответ :

Нет решения, которое имеет только преимущества и не требует (как в реальной жизни, так и в вашем вопросе).
Вы действительно есть только три варианта здесь:

  1. использования родительского состояние и управления данными (в родительском компоненте и/или магазинах) состоянии
  2. использования детского и спрячь ребенок вам не нужны (так что найти другую анимацию раствор)
  3. использование состояние ребенка и признать, что государство потеряно для детей, не перерисовываются -

Вы можете проверить Redux для использования магазинов.

Длинного ответ

Если вы хотите

  • удалить ребенок из DOM (например, для создания анимации с ReactCSSTransitionGroup)
  • И держать проверяемую/непроверенную & встречной информации

Тогда вы не сможете сохранить это в дочернем состоянии. Состояние по определению связано с жизненным циклом компонента.Если он удален из DOM (= unmounted), вы потеряете всю информацию о состоянии.

Поэтому, если вы хотите сохранить эту информацию, вам нужно переместить эту информацию в состояние родительского компонента (для каждого ребенка явно).

working codepen here.

Некоторые другие заметки об этом коде:

  • Ваши компоненты ребенка <...Box> теперь стали чистыми = апатриды компоненты
  • дочерние компоненты вызова метода родителя, чтобы обновить проверил состояние или счетчика приращения. В этих методах они передают свой собственный ID
  • Лучше не хранить целые компоненты в состоянии, а только реквизиты, определяющие компоненты.
  • Таким образом, новое родительское состояние содержит массив объектов, и каждый объект имеет соответствующие данные гребного винта
  • В визуализации родителя: лучше фильтровать компоненты во-первых, перед созданием массива компонентов, подлежащие вынесенного
  • В parent вам нужны новые методы (onCheck() и onClick()), которые будут обновлять конкретный объект в массиве из состояния. Требуется некоторое специальное упражнение, чтобы гарантировать, что мы не напрямую мутируем государство здесь.
  • для setState(), вам не нужно делать Object.assign() и передать все состояние снова. Если вы предоставите объект только с измененными параметрами, реакция оставит остальные параметры нетронутыми.
+0

Я понимаю ваши рассуждения, но что, если состояние ребенка сложное с множеством внутренних состояний? Я имею в виду, как иметь внуков и прочее? Хранение данных в глобальном масштабе (например, в магазинах) усложняет работу, не так ли? – Mehran

+1

Нет решения, которое имеет только преимущества и недостатки (как в реальной жизни, так и в вашем вопросе). У вас действительно есть только 3 варианта: a) использовать родительское состояние и управлять своими данными; b) использовать дочернее состояние и скрыть детей, которые вам не нужны (так что найти другое решение для анимации); c) использовать дочернее состояние и принять, что это состояние потеряно для детей не перерисовываться - вы могли бы использовать магазины хорошо, вы можете проверить редукс. – wintvelt

+0

Вы должны были разместить это – Mehran

0

вместо удаления компонента из DOM полностью (как вы делаете через filter, просто скрыть его заменить children.filter следующим:.

{children.map((box, idx) => { 
     var show = !this.state.showOnlyChecked || this.refs[box.props.id].isChecked(); 
     return <span key={idx} style={{display: (show? 'inline-block': 'none')}}>{box}</span>; 
    })} 
+0

Я думал об этом взломе, но это не эквивалентно тому, что не содержит тегов! Например, если вы собираетесь использовать анимацию для скрытия дочерних компонентов, ваше решение не будет иметь одинакового вывода, фактически оставив их. – Mehran

Смежные вопросы