Skip to content

Commit 08c09d9

Browse files
theblondealexdannyhw
authored andcommitted
Added Zustand and state management option
1 parent 8f6977e commit 08c09d9

File tree

9 files changed

+99
-6
lines changed

9 files changed

+99
-6
lines changed

.changeset/twenty-chefs-burn.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'create-expo-stack': minor
3+
---
4+
5+
Added a StateManagement Question with zustand to start and potentially more to follow
6+
7+
- setup with a question prompt just before the internationalization prompt
8+
- --zustand flag to skip the prompt
9+
- Adds a StateManagement folder to the project with a zustandStore.ts file to start with

cli/src/commands/create-expo-stack.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,15 @@ const command: GluegunCommand = {
296296
});
297297
}
298298

299+
// State Management packages
300+
if (options.zustand) {
301+
// Add zustand package
302+
cliResults.packages.push({
303+
name: 'zustand',
304+
type: 'state-management'
305+
});
306+
}
307+
299308
// Internalization packages
300309
if (options.i18next) {
301310
cliResults.packages.push({
@@ -354,6 +363,12 @@ const command: GluegunCommand = {
354363
script += '--drawer+tabs ';
355364
}
356365
}
366+
367+
const stateManagementPackage = cliResults.packages.find((p) => p.type === 'state-management');
368+
369+
if (stateManagementPackage) {
370+
script += `--${stateManagementPackage.name} `;
371+
}
357372
} else {
358373
// Add the packages
359374
cliResults.packages.forEach((p) => {
@@ -408,6 +423,9 @@ const command: GluegunCommand = {
408423
const internalizationPackage = packages.find((p) => p.type === 'internationalization');
409424
const analyticsPackage = packages.find((p) => p.type === 'analytics');
410425

426+
//add the state management package if it is selected
427+
const stateManagementPackage = packages.find((p) => p.type === 'state-management') || undefined;
428+
411429
let files: string[] = [];
412430

413431
files = configureProjectFiles(
@@ -418,7 +436,8 @@ const command: GluegunCommand = {
418436
analyticsPackage,
419437
toolbox,
420438
cliResults,
421-
internalizationPackage
439+
internalizationPackage,
440+
stateManagementPackage
422441
);
423442

424443
// Once all the files are defined, format and generate them
@@ -434,7 +453,8 @@ const command: GluegunCommand = {
434453
packageManager,
435454
stylingPackage,
436455
toolbox,
437-
internalizationPackage
456+
internalizationPackage,
457+
stateManagementPackage
438458
);
439459

440460
await printOutput(cliResults, formattedFiles, toolbox, stylingPackage);

cli/src/templates/base/package.json.ejs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
"@react-native-community/slider": "4.5.2",
6868
<% } %>
6969
<% } %>
70+
71+
<% if (props.stateManagementPackage?.name === "zustand") { %>
72+
"zustand": "^4.5.1",
73+
<% } %>
7074
7175
<% if (props.stylingPackage?.name === "restyle") { %>
7276
"@shopify/restyle": "^2.4.2",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { create } from 'zustand'
2+
3+
export interface BearState {
4+
bears: number
5+
increasePopulation: () => void
6+
removeAllBears: () => void
7+
updateBears: (newBears: number) => void
8+
}
9+
10+
export const useStore = create<BearState>((set) => ({
11+
bears: 0,
12+
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
13+
removeAllBears: () => set({ bears: 0 }),
14+
updateBears: (newBears) => set({ bears: newBears }),
15+
}))

cli/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const availablePackages = [
2626
'restyle',
2727
'unistyles',
2828
'i18next',
29+
'zustand',
2930
'vexo-analytics'
3031
] as const;
3132

@@ -39,6 +40,8 @@ export type StylingSelect = 'nativewind' | 'restyle' | 'stylesheet' | 'tamagui'
3940

4041
export type PackageManager = 'yarn' | 'npm' | 'pnpm' | 'bun';
4142

43+
export type StateManagementSelect = 'zustand' | undefined;
44+
4245
export type Internalization = 'i18next';
4346

4447
export type Analytics = 'vexo-analytics';
@@ -59,7 +62,7 @@ export type SelectedComponents =
5962

6063
export type AvailablePackages = {
6164
name: (typeof availablePackages)[number];
62-
type: 'navigation' | 'styling' | 'authentication' | 'internationalization' | 'analytics';
65+
type: 'navigation' | 'styling' | 'authentication' | 'internationalization' | 'state-management' | 'analytics';
6366
options?: { selectedComponents?: SelectedComponents[]; type?: NavigationTypes };
6467
};
6568

cli/src/utilities/configureProjectFiles.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export function configureProjectFiles(
2121
analyticsPackage: AvailablePackages | undefined,
2222
toolbox: Toolbox,
2323
cliResults: CliResults,
24-
internalizationPackage: AvailablePackages | undefined
24+
internalizationPackage: AvailablePackages | undefined,
25+
stateManagementPackage: AvailablePackages | undefined
2526
): string[] {
2627
// Define the files common to all templates to be generated
2728
let baseFiles = [
@@ -349,6 +350,12 @@ export function configureProjectFiles(
349350
}
350351
}
351352

353+
// add state management files if needed
354+
if (stateManagementPackage?.name === 'zustand') {
355+
const zustandFiles = ['packages/zustand/store/store.ts.ejs'];
356+
files = [...files, ...zustandFiles];
357+
}
358+
352359
// Add npmrc file if user is using pnpm
353360
if (packageManager === 'pnpm') {
354361
files.push('base/.npmrc.ejs');

cli/src/utilities/generateProjectFiles.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export function generateProjectFiles(
1111
packageManager: PackageManager,
1212
stylingPackage: AvailablePackages | undefined,
1313
toolbox: Toolbox,
14-
internalizationPackage: AvailablePackages | undefined
14+
internalizationPackage: AvailablePackages | undefined,
15+
stateManagementPackage: AvailablePackages | undefined
1516
) {
1617
const { projectName, packages, flags } = cliResults;
1718

@@ -27,6 +28,11 @@ export function generateProjectFiles(
2728
target = target.replace('packages/firebase/', '');
2829
}
2930

31+
//state management
32+
if (stateManagementPackage?.name === 'zustand') {
33+
target = target.replace('packages/zustand/', '');
34+
}
35+
3036
target = target.replace('base/', '');
3137

3238
if (stylingPackage?.name === 'tamagui') {
@@ -79,7 +85,8 @@ export function generateProjectFiles(
7985
packageManager,
8086
packages,
8187
stylingPackage,
82-
internalizationPackage
88+
internalizationPackage,
89+
stateManagementPackage
8390
}
8491
});
8592

cli/src/utilities/runCLI.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { semver } from 'gluegun';
55
import { bunInstallationError, defaultOptions, nativeWindUIOptions } from '../constants';
66
import {
77
AuthenticationSelect,
8+
StateManagementSelect,
89
CliResults,
910
NavigationSelect,
1011
NavigationTypes,
@@ -327,6 +328,32 @@ export async function runCLI(toolbox: Toolbox, projectName: string): Promise<Cli
327328
);
328329
}
329330

331+
const stateManagementSelect = await select({
332+
message: 'What would you like to use for state management?',
333+
options: [
334+
{ value: undefined, label: 'None' },
335+
{ value: 'zustand', label: 'Zustand' }
336+
// { value: 'mobx', label: 'MobX' },
337+
// { value: 'redux', label: 'Redux' },
338+
]
339+
});
340+
341+
if (isCancel(stateManagementSelect)) {
342+
cancel('Cancelled... 👋');
343+
return process.exit(0);
344+
}
345+
346+
if (stateManagementSelect) {
347+
cliResults.packages.push({
348+
name: stateManagementSelect as StateManagementSelect,
349+
type: 'state-management'
350+
});
351+
352+
success(`You'll be using ${stateManagementSelect} for state management.`);
353+
} else {
354+
success(`No problem, skipping state management for now.`);
355+
}
356+
330357
const authenticationSelect = await select({
331358
message: 'What would you like to use for authentication?',
332359
options: [

docs/src/content/docs/en/installation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,6 @@ bun create expo-stack myapp --expo-router --nativewind --bun
7373
| `--tamagui` | Use Tamagui for styling |
7474
| `--restyle` | Use Restyle for styling |
7575
| `--stylesheet` | Use StyleSheet for styling, used by default |
76+
| `--zustand` | Use Zustand for state management |
7677
| `--i18next` | Use i18next for internationalization |
7778
| `-i`, `--ignite` | Initialize an opinionated starter using Infinite Red's Ignite |

0 commit comments

Comments
 (0)