-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add-use-state: Create useState magic #4417
Open
MaquinaTech
wants to merge
2
commits into
alpinejs:main
Choose a base branch
from
MaquinaTech:add-use-state
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { useState } from '../useState' | ||
import { magic } from '../magics' | ||
|
||
magic('useState', (initialValue) => useState(initialValue)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export function useState(initialState = '') { | ||
let state = Alpine.reactive({ value: initialState }); | ||
|
||
const setState = (newValue) => { | ||
state.value = typeof newValue === 'function' ? newValue(state.value) : newValue; | ||
}; | ||
|
||
return { | ||
get state() { | ||
return state.value; | ||
}, | ||
setState | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
--- | ||
order: 10 | ||
prefix: $ | ||
title: useState | ||
--- | ||
|
||
# $useState | ||
|
||
`$useState` is a magic function that allows you to read and set data in variables. | ||
|
||
```alpine | ||
<div x-data="{ title: $useState('Hello') }"> | ||
<button | ||
@click="title.setState('Hello World!')" | ||
x-text="title.state" | ||
></button> | ||
</div> | ||
``` | ||
|
||
In the example above, the default value of `title` is set using `$useState('Hello')`. The variable is updated with `title.setState('Hello World!')`, and its value is accessed with `title.state`. | ||
|
||
## Initial State | ||
|
||
You can initialize the state with any value, including objects and arrays: | ||
|
||
```alpine | ||
<div x-data="{ user: $useState({ name: 'John', age: 30 }) }"> | ||
<button | ||
@click="user.setState({ name: 'Jane', age: 25 })" | ||
x-text="user.state.name" | ||
></button> | ||
</div> | ||
``` | ||
|
||
## Reactive Updates | ||
|
||
The state is reactive, meaning any changes to the state will automatically update the DOM elements that depend on it. This reactivity extends deeply, so if you pass the state variable as a parameter and modify it within a function, the changes will propagate and update the DOM as if it were an input/output variable. | ||
|
||
```alpine | ||
<div x-data="{ count: $useState(0) }"> | ||
<button | ||
@click="increment(count)" | ||
x-text="count.state" | ||
></button> | ||
</div> | ||
|
||
<script> | ||
function increment(state) { | ||
state.setState(state.state + 1); | ||
} | ||
</script> | ||
``` | ||
|
||
In this example, the `increment` function takes the state variable `count` as a parameter and updates its value. The DOM automatically reflects the updated state. | ||
|
||
## Accessing State | ||
|
||
You can access the current state using the `.state` property and update it using the `.setState` method. | ||
|
||
## Example with Array | ||
|
||
```alpine | ||
<div x-data="{ items: $useState(['Item 1', 'Item 2']) }"> | ||
<button | ||
@click="items.setState([...items.state, 'Item 3'])" | ||
x-text="items.state.join(', ')" | ||
></button> | ||
</div> | ||
``` | ||
|
||
In this example, a new item is added to the array, and the DOM updates to reflect the change. | ||
|
||
## Benefits | ||
|
||
### Input/Output Variables | ||
|
||
One of the key benefits of using `$useState` is the ability to treat state variables as input/output variables. This means you can pass them around in functions and have their changes automatically propagate through the DOM, enhancing the reactivity of your application. | ||
|
||
### Enhanced Security | ||
|
||
Another significant advantage is that `$useState` helps in complying with Content Security Policy (CSP) guidelines. By avoiding inline scripts and using safer methods to manage state, your application becomes more secure and less vulnerable to certain types of attacks. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { haveAttr, html, test } from '../../utils' | ||
|
||
test('useState initializes state with the given initial value', | ||
html` | ||
<div x-data="{ state: $useState('testValue') }" x-init="$el.setAttribute('x-data', state)"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All your tests fail |
||
</div> | ||
`, | ||
({ get }) => { | ||
get('[x-data]').should(haveAttr('x-data', 'testValue')) | ||
} | ||
) | ||
|
||
test('useState updates state correctly', | ||
html` | ||
<div x-data="{ state: $useState('initialValue') }" x-init="$el.setAttribute('x-data', state)"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this init even doing? It will just set x-data = |
||
<button @click="state('updatedValue')">Update</button> | ||
</div> | ||
`, | ||
({ get }) => { | ||
get('[x-data]').should(haveAttr('x-data', 'initialValue')) | ||
get('button').click() | ||
get('[x-data]').should(haveAttr('x-data', 'updatedValue')) | ||
} | ||
) | ||
|
||
test('useState reacts to state changes', | ||
html` | ||
<div x-data="{ state: $useState('initialValue') }" x-init="$el.setAttribute('x-data', state)"> | ||
<button @click="state('updatedValue')">Update</button> | ||
</div> | ||
`, | ||
({ get }) => { | ||
cy.wait(1000) // Espera 1 segundo para asegurarte de que Alpine.js se haya inicializado | ||
get('[x-data]').should(haveAttr('x-data', 'initialValue')) | ||
get('button').click() | ||
get('[x-data]').should(haveAttr('x-data', 'updatedValue')) | ||
} | ||
) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 looks like you're adding it to the core...
not as a plugin...
so which is it?
Definitely should not be a built in.