Skip to content

Comments

Feature/zustand#92

Merged
interim17 merged 19 commits intomainfrom
feature/zustand
Oct 8, 2025
Merged

Feature/zustand#92
interim17 merged 19 commits intomainfrom
feature/zustand

Conversation

@interim17
Copy link
Contributor

@interim17 interim17 commented Sep 23, 2025

Problem

Closes #95
Advances #83
Advances #97

Solution

Locally state that is passed as props and spread across multiple components reliably leads to bugs and anti-patterns.

In this PR we establish a zustand store for the recipe data, and a few pieces of UI related state. Packing state, is an example of something that could be moved into the store in the future.

Primarily this PR established the store and its use across the components.
It also runs a prefetch where on load all the recipe data is fetched from firebase and stored, to prevent numerous round trips to the back end. A small bit of "Loading..." ui read out was added to account for this while the page is loading.

Establishing the store as a clearinghouse for shared recipe data is a good first step to migrating away from using the JSON string as the single source of truth.

This PR installs and uses lodash-es's set and get functionality for working with the properties on the JSON in lieu of building custom utilities.

  • New feature (non-breaking change which adds functionality)

@github-actions
Copy link

github-actions bot commented Sep 23, 2025

PR Preview Action v1.6.2
Preview removed because the pull request was closed.
2025-10-08 16:30 UTC

@interim17 interim17 marked this pull request as ready for review October 1, 2025 21:24
@interim17 interim17 requested review from ascibisz and rugeli October 1, 2025 21:24
const handleInputChange = (s: string) => {
if (!selectedRecipeId) return;
setValue(s);
updateRecipeObj(selectedRecipeId, { [id]: s });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the code for handleInputChange and handleSelectChange are identical, maybe we could just have one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consolidated all three change handlers!

}
get().updateRecipeString(recipeId, JSON.stringify(obj, null, 2));
} catch {
/* ignore */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth adding a console log or anything here? Just as we're continuing to add more recipes and potentially face more issues, perhaps it could be good to have some error logs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, don't know how involved to get on this PR but at least a log works!

variant="filled"
disabled={!submitEnabled}
style={{ width: '100%' }}
disabled={isPacking || !submitEnabled}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the new isPacking removes the need for submitEnabled, so I think we could remove submitEnabled entirely

return { currentGradient: defaultValue, selectedOption: { value: defaultValue, display_name: defaultValue, path: "" } as GradientOption };
}

// 2) Shared selector path (all options for this control share it)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it looks like you skipped 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops yeah edited out and didn't fix the other numbers!

@interim17 interim17 requested a review from ascibisz October 6, 2025 22:20
Copy link
Contributor

@ascibisz ascibisz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!!

updateRecipeString: (recipeId: string, newString: string) => void;
updateRecipeObj: (recipeId: string, updates: Record<string, string | number>) => void;
restoreRecipeDefault: (recipeId: string) => void;
getCurrentValue: (path: string) => string | number | undefined; // STABLE
Copy link
Contributor

@rugeli rugeli Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you help me understand the STABLE comment a bit more? from what I can tell, we have values in recipes that are objects and arrays (e.g. color, gradient.direction, etc), so it seems like lodashGet in getCurrentValue could return more than str or num.

(Oh i see, we have type guard to handle non-scalar values in GradientInput, but not in InputSwitch. Should we add a type guard in getCurrentValue itself, similar to what getOriginalValue has?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that's really a useful comment, I'm going to remove it.

I think you're probably right that we should also have a type guard there to be sure we are using the library carefully!

Copy link
Contributor

@rugeli rugeli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for the initial work! It took me a bit to trace through the workflow (jsonToString-store-componets-edits). only had a non-blocking question about lodash return values and type guard along the way.

@interim17
Copy link
Contributor Author

Looks good, thanks for the initial work! It took me a bit to trace through the workflow (jsonToString-store-componets-edits). only had a non-blocking question about lodash return values and type guard along the way.

Thanks for the review! I'm sure we can continue to refine this over time, but it's something to build off of. :)

@github-actions
Copy link

github-actions bot commented Oct 8, 2025

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 24.89% 368 / 1478
🔵 Statements 24.89% 368 / 1478
🔵 Functions 38.98% 23 / 59
🔵 Branches 72.07% 80 / 111
File Coverage
File Stmts % Branch % Funcs % Lines Uncovered Lines
Changed Files
src/App.tsx 0% 0% 0% 0% 1-13, 15-16, 18-23, 25, 27-29, 31-34, 36-45, 47-63, 65-80, 82-84, 86-104, 106-110, 112-117, 119-131, 133, 135
src/components/GradientInput/index.tsx 0% 0% 0% 0% 1, 8-10, 17, 19-23, 25, 27-31, 33, 35-38, 41-46, 48-53, 55-58, 60-98, 100, 102, 104
src/components/InputSwitch/index.tsx 0% 0% 0% 0% 1-2, 9-11, 26-27, 29-32, 35-38, 41, 44-46, 48-52, 54-58, 60-82, 84, 86-103, 105, 107-114, 116, 118, 120-132, 134-136, 138
src/components/PackingInput/index.tsx 0% 0% 0% 0% 1, 13-17, 24-30, 32-36, 38-41, 44-46, 48-50, 52-56, 58-60, 62-81, 83, 85
src/components/RecipeForm/index.tsx 0% 0% 0% 0% 1-3, 8, 14-17, 19-39, 41-48, 50, 52, 54-55
src/state/store.ts 0% 0% 0% 0% 1, 44-51, 53-55, 57-65, 68-82, 84, 86-87, 89-92, 94-100, 102-104, 106-109, 111-114, 117-132, 134-136, 138-139, 141-145, 147-149, 151-154, 157-169, 173-184, 186-198, 200, 203, 206-216, 219-226
src/utils/gradient.ts 0% 100% 100% 0% 10, 14-16, 29-32, 37-40, 43, 46-47, 50-51, 53-54, 57-61, 63-64, 66-67, 69-75, 77-87
Generated in workflow #70

@interim17 interim17 merged commit ebf55af into main Oct 8, 2025
2 checks passed
@interim17 interim17 deleted the feature/zustand branch October 8, 2025 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Load All Firebase Recipe Data on Page Load

3 participants