Ответ нашел сам.
При сравнении старого и нового DOM, React сравнивает поле key у существующего и нового объекта, если значения
полей совпадают, то новый объект отбрасывается, иначе старый объект будет уничтожен и полностью замещен новым. Конечно же сравнение ключей лишь малая часть общего алгоритма согласования.
Моем случае, компоненты, которые отображаются в табах могут быть разных типов, а чаще всего только так и есть.
Поэтому React не использует сравнение через key и удаляет оставшиеся закладки (
хотя! почему то, если создать
вкладки одного типа, они все равно удалятся)
Решение найдено в использовании компонента Fragment с суррогатным ключом key
Генерация ключа:
const [keyGenValue, setKeyGenValue] = useState(1);
...
// генерим новое значение при открытии новой вкладки
setKeyGenValue(() => {
return keyGenValue + 1;
});
Генерация рендера:
<div className='workspace'>
{views.map(
(view, index) =>
<Fragment key={view.key}>
<div hidden={view !== selectedView}>
<h4>{view.caption}</h4>
<div>{buildView(view.name)}</div>
</div>
</Fragment>
)}
</div>