className={undefined} leaves an empty class="" attribute after client rendering, so [class] selectors match
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 84/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- next.js, react, typescript
- Domínio
- frontend
Direção de pesquisa
Comece em packages/redact/src/dom/dom.ts, na atribuição de className mostrada na issue; em seguida, execute a reprodução fornecida do Next.js e inspecione os atributos class no console do navegador. O trabalho estará concluído quando um className null ou undefined não deixar nenhum atributo class, enquanto um className com uma string vazia ainda produzir class="", correspondendo ao React.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Version: @tanstack/redact 0.2.6 through withRedact, Next.js 16.4.4 (App Router, Turbopack), react/react-dom 19.3.1, Chrome 156, production build. dom.ts is unchanged on main (049de24f).
What happens
When an HTML element is created or updated in the browser with className={undefined}, Redact leaves an empty class="" attribute. React leaves no attribute at all.
The cause is in packages/redact/src/dom/dom.ts:
(el as HTMLElement).className = next == null ? '' : '' + next;
Assigning an empty string to className creates the attribute.
| Element | React 19.3.1 | Redact 0.2.6 |
|---|---|---|
className={undefined}, server-rendered and hydrated |
no attribute | no attribute |
className={undefined}, server-rendered, then re-rendered |
no attribute | no attribute |
className={undefined}, rendered only in the browser |
no attribute | class="" |
className changes from 'x' to undefined |
no attribute | class="" |
className="" |
class="" |
class="" |
Why it matters
Selectors that test whether the attribute exists, such as section[class] or :not([class]), now match differently. CSS Modules make this easy to hit, because styles.name is undefined when a class has no rules.
We found it by comparing screenshots of our app against React baselines. An A/A run of React against itself was clean. A global section[class] rule started to apply to one section, which gained 70 px of block padding and a background, and the page grew 140 px taller. Removing the empty attribute in the browser restored the React layout exactly.
Together with #85 and #81, this keeps us from using Redact in production on Next.js.
Reproduction
package.json dependencies: [email protected], [email protected], [email protected], @tanstack/[email protected].
next.config.mjs:
import { withRedact } from '@tanstack/redact/next'
export default withRedact({})
app/layout.js:
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
app/page.js:
'use client'
import { useEffect, useState } from 'react'
export default function Page() {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
return (
<main>
<section id="ssr-undefined" className={undefined}>a</section>
<section id="ssr-empty" className="">b</section>
<section id="rerender-undefined" className={undefined} data-n={mounted ? 1 : 0}>c</section>
<section id="toggle-to-undefined" className={mounted ? undefined : 'x'}>d</section>
{mounted ? <section id="client-undefined" className={undefined}>e</section> : null}
{mounted ? <section id="client-empty" className="">f</section> : null}
</main>
)
}
Run next build && next start, open /, and run this in the console:
Object.fromEntries([...document.querySelectorAll('section')].map((e) => [e.id, e.getAttribute('class')]))
Redact 0.2.6:
{"ssr-undefined":null,"ssr-empty":"","rerender-undefined":null,"toggle-to-undefined":"","client-undefined":"","client-empty":""}
React 19.3.1:
{"ssr-undefined":null,"ssr-empty":"","rerender-undefined":null,"toggle-to-undefined":null,"client-undefined":null,"client-empty":""}
Expected
The same result as React: a null or undefined className leaves no class attribute.
Suggested fix
if (next == null) el.removeAttribute('class');
else (el as HTMLElement).className = '' + next;
- Linguagem predominante
- TypeScript
- Estrelas
- 537
- Forks
- 22
- Merge médio
- 11h 28min
- PRs com merge (30d)
- 34
Preparar o ambiente
Este projeto não oferece contêiner de desenvolvimento, Dockerfile nem guia de contribuição, então a configuração fica por sua conta: comece pelo README e veja nosso guia da primeira contribuição para os passos gerais.
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de TanStack/redact
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 55/100
TanStack/redact#144 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 48/100
TanStack/redact#141 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 3/5 1-2 dias Facilidade para iniciantes 68/100
TanStack/redact#139 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
Todas as issues de TanStack/redact
Issues semelhantes
-
test: claim-conflict drop leaves both facts active and is never re-proposed (untested review cell)Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 83/100
Mantenedores costumam responder em até 1 dia
-
[Feature]: Usage page should show API-equivalent cost and say when a connected plan shows info onlyAbertaenhancement from-x
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 66/100
nightly-labs/openbot#4867 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
egonSchiele/agency-lang#1377 ·
Mantenedores costumam responder em até 1 dia
-
normalizePath removes the Windows device namespace prefixTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
caprover/caprover-frontend#273 ·
Mantenedores costumam responder em até 1 dia