-
Notifications
You must be signed in to change notification settings - Fork 664
Conversation
Deploying with Cloudflare Pages
|
Could you add some screenshots or videos about the change of this pr? |
Sorry, I didn't see the preview playground |
I would still find it useful to have a proper test summary because it safes me time as a reviewer:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<TreeView tree={cst} /> | ||
<TreeView | ||
treeStyle={treeStyle} | ||
setTreeStyle={setTreeStyle} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Events are commonly called onTreeStyleChanged
to align it with HTML event handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ehh I'd actually push back on this. I don't think it's more declarative to use the on
-prefix, as these functions aren't running when the tree style changes, they're making the tree style change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason why I would change it is because TreeView
now makes very specific assumption in what context its used. The signature implies that setTreeStyle
must set the tree style. It kind of takes the choice from the parent what it want's to do in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, how does TreeView
make an assumption about the context in which it's used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name setTreeStyle
implies that the using component has to set the treeStyle
just by how it's named. onTreeStyleChanged
on the other hand doesn't make any implication what the parent does in that case. It's like... here, this thing happened... do what ever you like, I don't care. Whereas setTreeStyle
reads more like... Hey, set the tree state.
website/playground/src/types.ts
Outdated
@@ -21,6 +22,8 @@ export interface PlaygroundState { | |||
setIsTypeScript: (isTypeScript: boolean) => void; | |||
isJsx: boolean; | |||
setIsJsx: (isJsx: boolean) => void; | |||
treeStyle: TreeStyle; | |||
setTreeStyle: Dispatch<SetStateAction<TreeStyle>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Change to plain function
website/playground/src/utils.ts
Outdated
@@ -98,6 +103,8 @@ export function usePlaygroundState(): PlaygroundState { | |||
setIsJsx, | |||
sourceType, | |||
setSourceType, | |||
treeStyle, | |||
setTreeStyle, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is becoming hard to navigate. Separating this into a single state object with a reducer that allows changing single fields would remove the fields by about 50%.
Another alternative is to store the values in a context and having a provider + consumer hook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah..I was considering gasp adding Redux because we're reaching the point where we have enough state such that Redux might be worth it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to add Redux just for that. A simple useContext
should do to have shared state or change it to useDispatcher
.
859124c
to
cde64e2
Compare
interface Props { settings: PlaygroundSettings } | ||
interface Props { | ||
settings: PlaygroundSettings; | ||
setPlaygroundState: Dispatch<SetStateAction<PlaygroundState>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MichaReiser I'm using the complicated type signature here because I'm making use of the setPlaygroundState(state => ...)
pattern
(searchParams.get("sourceType") as SourceType) ?? SourceType.Module, | ||
); | ||
const [treeStyle, setTreeStyle] = useState(TreeStyle.Json); | ||
const [playgroundState, setPlaygroundState] = useState({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to move this into a lambda so that the initialize code only runs for the first render:
const [..] = useState(() => ({....}))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah my bad, forgot to do that
export function createSetter( | ||
setPlaygroundState: Dispatch<SetStateAction<PlaygroundState>>, | ||
field: keyof PlaygroundState, | ||
) { | ||
return function (param: PlaygroundState[typeof field]) { | ||
setPlaygroundState((state) => ({ ...state, [field]: param })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what you're trying to replicate is useReducer
The idea is to have one action
for each field.
Overall, what you're trying to do here feels complicated with passing down the setPlaygroundState
and then having a createSetter
. For someone to understand the code they must first understand what setPlaygroundState
is, then what createSetter
does` for something that's otherwise relatively simple.
I think what you want here is useContext
which helps you avoid the prop drilling (passing down many props multiple level-deep).
The provider doesn't need to accept any props because it initializes the state from the URL params.
They consumer can then expose the state + a method to change a single field
const [{ code }, dispatchPlaygroundAction] = usePlaygroundState();
dispatchPlaygroundAction("change_code", code);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ehhh. I just did the useReducer refactor with useContext. It gets very hairy. First, useContext is quite annoying in that you can't call createContext
inside a component, so it doesn't play well with other hooks like useReducer. You have to initialize the context values to undefined/null, or initialize them in the context, but then re-initialize them in the reducer as well. If you initialize them to undefined/null, TypeScript gets very annoyed (understandably) about you using potentially undefined variables.
With actions, they are more explicit, but I could also easily make this more explicit by not using createSetter
and just using setter functions explicitly. In my experience useReducer
makes more sense when you need to do actions more complicated than set, i.e. actions where you change multiple parts of the state at once and need the abstraction of an action over just a setter. In this case while we have a lot of state, we're almost always just setting one field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having done this refactor just now, I have to say, it makes me a little more pro-Redux. With the starter kit, Redux is not much more overhead than useReducer
+ useContext
and it avoids all of these nasty undefined/null issues.
We can do the refactor changes as part of another PR. Please update your test plan with a screenshot for Mobile/Desktop. |
bd71c68
to
9a29771
Compare
Summary
As some have pointed out, the JSON view is not optimal for some situations. This allows users to toggle between the text and JSON view.
Test Plan