Skip to content

Commit

Permalink
Fix chained .bind of Server Actions (#49874)
Browse files Browse the repository at this point in the history
This makes sure that you can `.bind` a Server Action multiple times. Thanks to the feedback from @sophiebits:  #49422 (review)
  • Loading branch information
shuding committed May 16, 2023
1 parent 9e51169 commit 4970811
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 7 deletions.
Expand Up @@ -4,27 +4,34 @@ export default function createActionProxy(
action: any,
originalAction?: any
) {
action.$$typeof = Symbol.for('react.server.reference')
action.$$id = id
action.$$bound = bound
function bindImpl(this: any, _: any, ...boundArgs: any[]) {
const currentAction = this

action.bind = function (_: any, ...boundArgs: any[]) {
const newAction = async function (...args: any[]) {
if (originalAction) {
return originalAction(newAction.$$bound.concat(args))
} else {
// In this case we're calling the user-defined action directly.
return action(...newAction.$$bound, ...args)
return currentAction(...newAction.$$bound, ...args)
}
}

for (const key of ['$$typeof', '$$id', '$$FORM_ACTION']) {
// @ts-ignore
newAction[key] = action[key]
newAction[key] = currentAction[key]
}

// Rebind args
newAction.$$bound = (action.$$bound || []).concat(boundArgs)
newAction.$$bound = (currentAction.$$bound || []).concat(boundArgs)

// Assign bind method
newAction.bind = bindImpl.bind(newAction)

return newAction
}

action.$$typeof = Symbol.for('react.server.reference')
action.$$id = id
action.$$bound = bound
action.bind = bindImpl.bind(action)
}
10 changes: 10 additions & 0 deletions test/e2e/app-dir/actions/app-action.test.ts
Expand Up @@ -123,6 +123,16 @@ createNextDescribe(
}, '/header?result=122')
})

it('should support chained .bind', async () => {
const browser = await next.browser('/server')

await browser.elementByCss('#add3').click()

await check(() => {
return browser.eval('window.location.pathname + window.location.search')
}, '/header?result=6')
})

it('should support notFound (javascript disabled)', async () => {
const browser = await next.browser('/server', {
// TODO we should also test this with javascript on but not-found is not implemented yet.
Expand Down
15 changes: 15 additions & 0 deletions test/e2e/app-dir/actions/app/server/form.js
Expand Up @@ -34,6 +34,11 @@ export default function Form() {
redirect('/header?result=' + (a + b + Number(formData.get('n'))))
}

async function add3(a, b, c) {
'use server'
redirect('/header?result=' + (a + b + c))
}

return (
<>
<hr />
Expand Down Expand Up @@ -66,6 +71,16 @@ export default function Form() {
-1
</button>
</form>
<hr />
<form>
<button
type="submit"
id="add3"
formAction={add3.bind(null, 1).bind(null, 2).bind(null, 3)}
>
add3
</button>
</form>
</>
)
}

0 comments on commit 4970811

Please sign in to comment.