From 25dd2c3c6611a85237feebe2275142b97632395a Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Tue, 19 Sep 2023 00:19:04 +0200 Subject: [PATCH] Update curried-produce.mdx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First, thanks for this library. I hope you'll find this slight rewriting accepatble. On first reading, I found https://immerjs.github.io/immer/curried-produce extremely confusing. The main reason being that, when I read "Passing a function as the first argument", I thought you meant that we were seeing a variation of the `produce` from the previous page, where both arguments were functions. I believe it's important to note that we'll see a very different way to call `produce`. It's even more confusing, because you introduce a new way to call `produce` with an argument, and your example does not use this new way. I spend quite some time trying to understand how this example works. I believe it is better to explain to the user that they are going to see the "bad" version of the example first. Then explain what's wrong with this code (too much boilerplate), and how it can be improved. Finally, once they saw the nice code, explain how it work. I also suspect that, in order to emphasize that `toggleTodo` consists simply of `produce` applied to the function that implements the actual business logic, it's nice to add this business logic as a separate function. This way, we get the very simple `const toggleTodo = produce(toggleTodo_)`. I don't state this would be good production code; I admit that in real life, an anonymous lambda would be better. However, for educational purpose, I find this clearer. This really emphasize that `toggleTodo` is semantically the exact same function as `toggleTodo_` except that it does not uses side effect but output a new value. --- website/docs/curried-produce.mdx | 33 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/website/docs/curried-produce.mdx b/website/docs/curried-produce.mdx index 0f84b7b3..9298acc6 100644 --- a/website/docs/curried-produce.mdx +++ b/website/docs/curried-produce.mdx @@ -30,16 +30,22 @@ title: Curried producers -Passing a function as the first argument to `produce` creates a function that doesn't apply `produce` yet to a specific state, but rather creates a function that will apply `produce` to any state that is passed to it in the future. This generally is called _currying_. Take for example the following example: +There is a second way to call `produce`. Let us first consider some code with too much boilerplate, and then how a technique known as __currying__ can improve our code. ```javascript import {produce} from "immer" +// Toggles the "todo" associated to `id` +// This is the actual business logic we care about +function toggleTodo_(state, id) { + const todo = state.find(todo => todo.id === id) + todo.done = !todo.done +} + +// Returns `state` with the "todo" associated to `id` toggled +// `state` remains unchanged function toggleTodo(state, id) { - return produce(state, draft => { - const todo = draft.find(todo => todo.id === id) - todo.done = !todo.done - }) + return produce(toggleTodo_) } const baseState = [ @@ -58,22 +64,15 @@ const baseState = [ const nextState = toggleTodo(baseState, "Immer") ``` -The above pattern of `toggleTodo` is quite typical; pass an existing state to `produce`, modify the `draft`, and then return the result. Since `state` isn't used for anything else than passing it on to `produce`, the above example can be simplified by using the _curried_ form of `produce`, where you pass `produce` only the recipe function, and `produce` will return a new function that will apply recipe to the base state. This allows us to shorten the above `toggleTodo` definition. +The above pattern of `toggleTodo` is quite typical; pass an existing state to `produce`, modify the `draft`, and then return the result. You are essentially applying `produce` on top of a function, `toggleTodo_` that implements the business logic. Since `state` isn't used for anything else than passing it on to `produce`, the above example can be simplified by using the _curried_ form of `produce` as shown below. ```javascript -import {produce} from "immer" - // curried producer: -const toggleTodo = produce((draft, id) => { - const todo = draft.find(todo => todo.id === id) - todo.done = !todo.done -}) - -const baseState = [ - /* as is */ -] +const toggleTodo = produce(toggleTodo_) const nextState = toggleTodo(baseState, "Immer") ``` -Note that the `id` param has now become part of the recipe function! This pattern of having curried producers combines really neatly with for example the `useState` hook from React, as we will see on the next page. +Here, you pass `produce` a single argument, a recipe that takes the draft state and the extra arguments of `toggleTodo`, in this case `id`, and obtain a function that is essentially equivalent to `toggleTodo_`, appart that, instead of updating its first argument, it returns the updated copy of it. It is important to note that, in this case, `produce` did not return a state, but a function. This function, which we called `toggleTodo`, will apply `toggleTodo_` to any state that is passed to it in the future. + +Note also that the `id` param has now become part of the recipe function! This pattern of having curried producers combines really neatly with for example the `useState` hook from React, as we will see on the next page.