or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

components.mdhocs.mdhooks.mdicu-macro.mdindex.mdssr.md

hocs.mddocs/

0

# Higher-Order Components

1

2

React i18next provides higher-order components (HOCs) for injecting translation functionality into class components and legacy React patterns, as well as server-side rendering support.

3

4

## Capabilities

5

6

### withTranslation HOC

7

8

Higher-order component that injects translation props into wrapped components, providing an alternative to hooks for class components or when hooks aren't suitable.

9

10

```typescript { .api }

11

/**

12

* HOC that injects translation props into wrapped components

13

* @param ns - Namespace(s) for translations

14

* @param options - Configuration options including ref forwarding and key prefix

15

* @returns HOC function that wraps components with translation props

16

*/

17

function withTranslation<

18

Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,

19

KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined

20

>(

21

ns?: Ns,

22

options?: {

23

/** Enable ref forwarding to wrapped component */

24

withRef?: boolean;

25

/** Prefix for all translation keys */

26

keyPrefix?: KPrefix;

27

}

28

): <

29

C extends React.ComponentType<React.ComponentProps<any> & WithTranslationProps>

30

>(

31

component: C

32

) => React.ComponentType<Omit<React.ComponentProps<C>, keyof WithTranslation<Ns>> & WithTranslationProps>;

33

34

// Props injected by withTranslation HOC

35

interface WithTranslation<

36

Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,

37

KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined

38

> {

39

/** Translation function with type-safe keys */

40

t: TFunction<FallbackNs<Ns>, KPrefix>;

41

/** i18next instance for language changes and configuration */

42

i18n: i18n;

43

/** Indicates if translations are loaded and ready */

44

tReady: boolean;

45

}

46

47

// Props that can be passed to wrapped component

48

interface WithTranslationProps {

49

/** Custom i18next instance */

50

i18n?: i18n;

51

/** Enable React Suspense mode */

52

useSuspense?: boolean;

53

}

54

```

55

56

**Usage Examples:**

57

58

```typescript

59

import { withTranslation, WithTranslation } from "react-i18next";

60

61

// Class component with injected translation props

62

interface Props extends WithTranslation {

63

username: string;

64

}

65

66

class UserProfile extends React.Component<Props> {

67

handleLanguageChange = (lang: string) => {

68

this.props.i18n.changeLanguage(lang);

69

};

70

71

render() {

72

const { t, tReady, username } = this.props;

73

74

if (!tReady) return <div>Loading...</div>;

75

76

return (

77

<div>

78

<h1>{t('profile.title')}</h1>

79

<p>{t('profile.welcome', { name: username })}</p>

80

<button onClick={() => this.handleLanguageChange('es')}>

81

{t('switchLanguage')}

82

</button>

83

</div>

84

);

85

}

86

}

87

88

export default withTranslation('user')(UserProfile);

89

90

// Functional component with HOC (alternative to useTranslation)

91

function Settings({ t, i18n, tReady }: WithTranslation) {

92

if (!tReady) return <div>Loading...</div>;

93

94

return (

95

<div>

96

<h2>{t('settings.title')}</h2>

97

<button onClick={() => i18n.reloadResources()}>

98

{t('settings.reload')}

99

</button>

100

</div>

101

);

102

}

103

104

export default withTranslation('common')(Settings);

105

106

// With namespace and key prefix

107

const TranslatedComponent = withTranslation('dashboard', {

108

keyPrefix: 'widgets'

109

})(function Widget({ t }) {

110

return <div>{t('title')}</div>; // Resolves to 'dashboard:widgets.title'

111

});

112

113

// With ref forwarding

114

class RefComponent extends React.Component {

115

focus() {

116

// Component method

117

}

118

119

render() {

120

const { t } = this.props;

121

return <input placeholder={t('placeholder')} />;

122

}

123

}

124

125

const TranslatedRefComponent = withTranslation('forms', {

126

withRef: true

127

})(RefComponent);

128

129

// Usage with ref

130

function Parent() {

131

const ref = useRef(null);

132

133

return (

134

<div>

135

<TranslatedRefComponent ref={ref} />

136

<button onClick={() => ref.current?.focus()}>

137

Focus Input

138

</button>

139

</div>

140

);

141

}

142

143

// Multiple namespaces

144

const MultiNSComponent = withTranslation(['common', 'user'])(

145

function Profile({ t }) {

146

return (

147

<div>

148

<h1>{t('user:title')}</h1>

149

<button>{t('common:save')}</button>

150

</div>

151

);

152

}

153

);

154

```

155

156

### withSSR HOC

157

158

Higher-order component that adds server-side rendering support to components, including getInitialProps method for data fetching.

159

160

```typescript { .api }

161

/**

162

* HOC providing SSR support with getInitialProps method

163

* @returns HOC function that adds SSR capabilities to wrapped components

164

*/

165

function withSSR(): <Props>(

166

WrappedComponent: React.ComponentType<Props>

167

) => {

168

(props: {

169

initialI18nStore: Resource;

170

initialLanguage: string;

171

} & Props): React.FunctionComponentElement<Props>;

172

getInitialProps: (ctx: unknown) => Promise<{

173

initialI18nStore: Resource;

174

initialLanguage: string;

175

}>;

176

};

177

```

178

179

**Usage Examples:**

180

181

```typescript

182

import { withSSR, WithTranslation, withTranslation } from "react-i18next";

183

184

// Component with SSR support

185

interface PageProps extends WithTranslation {

186

data: any;

187

}

188

189

class HomePage extends React.Component<PageProps> {

190

static async getInitialProps(ctx) {

191

// Fetch page-specific data

192

const data = await fetchPageData(ctx);

193

return { data };

194

}

195

196

render() {

197

const { t, data } = this.props;

198

return (

199

<div>

200

<h1>{t('home.title')}</h1>

201

<div>{data.content}</div>

202

</div>

203

);

204

}

205

}

206

207

// Combine with translation HOC and SSR HOC

208

export default withSSR()(withTranslation('home')(HomePage));

209

210

// Functional component with SSR

211

function ProductPage({ product, t, initialI18nStore, initialLanguage }) {

212

return (

213

<div>

214

<h1>{t('product.title', { name: product.name })}</h1>

215

<p>{t('product.price', { price: product.price })}</p>

216

</div>

217

);

218

}

219

220

ProductPage.getInitialProps = async (ctx) => {

221

const product = await fetchProduct(ctx.query.id);

222

return { product };

223

};

224

225

export default withSSR()(withTranslation('products')(ProductPage));

226

227

// Next.js usage

228

export default function Page(props) {

229

return <HomePage {...props} />;

230

}

231

232

export const getServerSideProps = HomePage.getInitialProps;

233

234

// Usage in Next.js App component

235

class MyApp extends App {

236

static async getInitialProps(appContext) {

237

const appProps = await App.getInitialProps(appContext);

238

239

// Get SSR translation data

240

const { Component } = appContext;

241

if (Component.getInitialProps) {

242

const pageProps = await Component.getInitialProps(appContext.ctx);

243

return { ...appProps, pageProps };

244

}

245

246

return appProps;

247

}

248

249

render() {

250

const { Component, pageProps } = this.props;

251

return <Component {...pageProps} />;

252

}

253

}

254

```

255

256

## HOC Composition and Patterns

257

258

### Combining HOCs

259

260

```typescript

261

// Multiple HOCs composition

262

const EnhancedComponent = withSSR()(

263

withTranslation('namespace')(

264

MyComponent

265

)

266

);

267

268

// With custom display names

269

function MyComponent({ t, customProp }) {

270

return <div>{t('title')} - {customProp}</div>;

271

}

272

273

const Enhanced = withTranslation('custom')(MyComponent);

274

Enhanced.displayName = 'TranslatedMyComponent';

275

276

// Type-safe HOC composition

277

interface ComponentProps {

278

customProp: string;

279

}

280

281

const TypedComponent: React.FC<ComponentProps & WithTranslation> = ({ t, customProp }) => (

282

<div>{t('message')} {customProp}</div>

283

);

284

285

export default withTranslation()(TypedComponent);

286

```

287

288

### Legacy Class Component Integration

289

290

```typescript

291

// Traditional class component with lifecycle methods

292

class LegacyComponent extends React.Component<WithTranslation & { userId: string }> {

293

componentDidMount() {

294

const { i18n, userId } = this.props;

295

296

// Load user-specific translations

297

i18n.loadNamespaces(['user-specific']);

298

}

299

300

componentDidUpdate(prevProps) {

301

const { i18n, userId } = this.props;

302

303

if (prevProps.userId !== userId) {

304

// Reload translations for new user

305

i18n.reloadResources();

306

}

307

}

308

309

render() {

310

const { t, tReady, userId } = this.props;

311

312

if (!tReady) {

313

return <div>{t('loading')}</div>;

314

}

315

316

return (

317

<div>

318

<h1>{t('user.welcome', { id: userId })}</h1>

319

<p>{t('user.status')}</p>

320

</div>

321

);

322

}

323

}

324

325

export default withTranslation(['common', 'user'])(LegacyComponent);

326

```

327

328

## Type Definitions

329

330

```typescript { .api }

331

// Helper types for component props manipulation

332

type $Subtract<T extends K, K> = Omit<T, keyof K>;

333

334

// Fallback namespace resolution

335

type FallbackNs<Ns> = Ns extends undefined

336

? TypeOptions['defaultNS']

337

: Ns extends Namespace

338

? Ns

339

: TypeOptions['defaultNS'];

340

341

// HOC return type helpers

342

type HOCResult<C, InjectedProps> = React.ComponentType<

343

Omit<React.ComponentProps<C>, keyof InjectedProps> & WithTranslationProps

344

>;

345

```

346

347

## Migration from Class Components

348

349

When migrating from HOCs to hooks:

350

351

```typescript

352

// Before: HOC pattern

353

class OldComponent extends React.Component<WithTranslation> {

354

render() {

355

const { t } = this.props;

356

return <div>{t('message')}</div>;

357

}

358

}

359

export default withTranslation()(OldComponent);

360

361

// After: Hook pattern

362

function NewComponent() {

363

const { t } = useTranslation();

364

return <div>{t('message')}</div>;

365

}

366

export default NewComponent;

367

```

368

369

## Performance Considerations

370

371

- HOCs create wrapper components that may affect React DevTools display

372

- Use `withRef: true` when you need to access wrapped component methods

373

- Consider hooks for new components as they have less overhead

374

- HOCs are still valuable for class components and complex composition patterns