Skip to content

Instantly share code, notes, and snippets.

@andreidmt
Last active December 29, 2022 09:01
Show Gist options
  • Select an option

  • Save andreidmt/dbc1accfc41c422d09d0d92af17634b7 to your computer and use it in GitHub Desktop.

Select an option

Save andreidmt/dbc1accfc41c422d09d0d92af17634b7 to your computer and use it in GitHub Desktop.
`useCrudState` via ChatGPT - part 1

I have various versions of useCrudState hook around but never published a package of it. I used ChatGPT to see how far I can get and what the experience is by "just dictating".

I needed multiple sessions since at some point ChatGPT got stuck middle-way and stopped outputting. In between sessions, I sometimes reordered or do some cosmetics that I would do faster.

While Functional Programming teaches you to focus on more "what" as oposed to "how", working together with ChatGPT takes this to a different level.
You tell it to do something and it does it but not exactly how you expected or wanted. You re-read your instructions and realize that you were not clear enough or just a wrong comma. You rephrase and realize your reasoning was crooked, used to many words etc. You go back 3 questions and reshape your argument. This is the ultimate rubber ducking, when the duck answers back and helps you obtain a better resolution on the overall chain/funnel of transformation.

Overall, I cant wait for other interfaces to emerge. Copilot is great but it's still at the snippet level and I dont think there are any inherent limitations to the tech to not work at a project level. For ex. coding in your editor while also telling it to "install and setup babel", "switch to swc" or "similar to the admin section of Users, write the pages for Todos" etc.

Resulting code at https://github.com/asd-xiv/use-crud-state


Q1: Generate a React Hook called useCrudState, similar with the native useState hook, that gives the ability of editing the internal array state through all the CRUD methods instead of useState just one method.

Certainly! Here is an example of how you could implement a useCrudState hook in React:

import { useState } from 'react';

function useCrudState(initialState) {
  const [state, setState] = useState(initialState);

  function add(item) {
    setState([...state, item]);
  }

  function remove(index) {
    setState([...state.slice(0, index), ...state.slice(index + 1)]);
  }

  function update(index, item) {
    setState([...state.slice(0, index), item, ...state.slice(index + 1)]);
  }

  return [state, { add, remove, update }];
}

This hook works just like the native useState hook, but instead of just providing a setter function for updating the state, it also provides add, remove, and update functions for performing CRUD operations on the state.

You can use this hook in your React components like this:

import { useCrudState } from './useCrudState';

function MyComponent() {
  const [items, { add, remove, update }] = useCrudState([]);

  // use add, remove, and update to perform CRUD operations on the items state
}

I hope this helps! Let me know if you have any questions.

Q2: Use only arrow functions, rename add to create and instead of exposing the raw items array, use selectors for accessing all the items.

Certainly! Here is an updated version of the useCrudState hook that uses arrow functions and exposes selectors for accessing the items in the state:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const create = item => {
    setState([...state, item]);
  };

  const remove = index => {
    setState([...state.slice(0, index), ...state.slice(index + 1)]);
  };

  const update = (index, item) => {
    setState([...state.slice(0, index), item, ...state.slice(index + 1)]);
  };

  const all = () => state;

  return [{ create, remove, update }, { all }];
};

This hook works just like the previous version, but it uses arrow functions instead of regular functions, and it exposes selectors for accessing the items in the state.

You can use this hook in your React components like this:

import { useCrudState } from './useCrudState';

function MyComponent() {
  const [{ create, remove, update }, { all }] = useCrudState([]);

  // use create, remove, and update to perform CRUD operations on the state
  // use the all selector to access all the items in the state
}

I hope this helps! Let me know if you have any questions.

Q3: Reorder the selector and setters, first the selectors, similar to how React's useState.

Certainly! Here is an updated version of the useCrudState hook that reorders the selectors and setters, so that the selectors are first, similar to how the useState hook works:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const all = () => state;

  const create = item => {
    setState([...state, item]);
  };

  const remove = index => {
    setState([...state.slice(0, index), ...state.slice(index + 1)]);
  };

  const update = (index, item) => {
    setState([...state.slice(0, index), item, ...state.slice(index + 1)]);
  };

  return [all, { create, remove, update }];
};

This hook works just like the previous versions, but it reorders the selectors and setters so that the selectors are first.

You can use this hook in your React components like this:

import { useCrudState } from './useCrudState';

function MyComponent() {
  const [all, { create, remove, update }] = useCrudState([]);

  // use all to access all the items in the state
  // use create, remove, and update to perform CRUD operations on the state
}

Q4: Update the hook to expect the state is composed of only objects that always have an id field. Update the remove and update setters to work based on the id field.

Certainly! Here is an updated version of the useCrudState hook that expects the state to be composed of objects with an id field, and that updates the remove and update setters to work based on the id field:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const all = () => state;

  const create = item => {
    setState([...state, item]);
  };

  const remove = id => {
    setState(state.filter(item => item.id !== id));
  };

  const update = (id, update) => {
    setState(
      state.map(item => (item.id === id ? { ...item, ...update } : item))
    );
  };

  return [all, { create, remove, update }];
};

This hook works just like the previous versions, but it expects the state to be composed of objects with an id field, and it updates the remove and update setters to work based on the id field.

You can use this hook in your React components like this:

import { useCrudState } from './useCrudState';

function MyComponent() {
  const [all, { create, remove, update }] = useCrudState([]);

  // use all to access all the items in the state
  // use create, remove, and update to perform CRUD operations on the state
}

I hope this helps! Let me know if you have any questions.

Q5: Add another selector that allows retrieving an item by id.

Certainly! Here is an updated version of the useCrudState hook that also includes a selector for retrieving an item by id:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const all = () => state;

  const getById = id => state.find(item => item.id === id);

  const create = item => {
    setState([...state, item]);
  };

  const remove = id => {
    setState(state.filter(item => item.id !== id));
  };

  const update = (id, update) => {
    setState(
      state.map(item => (item.id === id ? { ...item, ...update } : item))
    );
  };

  return [all, { create, remove, update, getById }];
};

This hook works just like the previous versions, but it also includes a getById selector for retrieving an item by its id field.

You can use this hook in your React components like this:

import { useCrudState } from './useCrudState';

function MyComponent() {
  const [all, { create, remove, update, getById }] = useCrudState([]);

  // use all to access all the items in the state
  // use create, remove, and update to perform CRUD operations on the state
  // use getById to retrieve an item by its id
}

I hope this helps! Let me know if you have any questions.

Q6: Keep all selectors and setters grouped in separate objects from now on.

Certainly! Here is an updated version of the useCrudState hook that keeps all the selectors and setters grouped in separate objects:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const selectors = {
    all: () => state,
    getById: id => state.find(item => item.id === id)
  };

  const setters = {
    create: item => {
      setState([...state, item]);
    },
    remove: id => {
      setState(state.filter(item => item.id !== id));
    },
    update: (id, update) => {
      setState(
        state.map(item => (item.id === id ? { ...item, ...update } : item))
      );
    }
  };

  return [selectors, setters];
};

This hook works just like the previous versions, but it keeps all the selectors and setters grouped in separate objects.

You can use this hook in your React components like this:

import { useCrudState } from './useCrudState';

function MyComponent() {
  const [selectors, setters] = useCrudState([]);

  // use selectors.all to access all the items in the state
  // use setters.create, setters.remove, and setters.update to perform CRUD operations on the state
  // use selectors.getById to retrieve an item by its id
}

I hope this helps! Let me know if you have any questions.

Q7: From now on, skip the 2nd code sample, the how to use the hook example, keep your answers as short as possible, unless I ask you to explain.

Certainly! I apologize for the lengthy explanations. From now on, I will skip the second code sample (the example of how to use the hook in a React component) and keep my answers as short as possible, unless you ask me to explain something in more detail.

Here is the updated version of the useCrudState hook that keeps all the selectors and setters grouped in separate objects:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const selectors = {
    all: () => state,
    getById: id => state.find(item => item.id === id)
  };

  const setters = {
    create: item => {
      setState([...state, item]);
    },
    remove: id => {
      setState(state.filter(item => item.id !== id));
    },
    update: (id, update) => {
      setState(
        state.map(item => (item.id === id ? { ...item, ...update } : item))
      );
    }
  };

  return [selectors, setters];
};

I hope this helps! Let me know if you have any questions.

Q8: Update the create setter to throw an error if an item with the same id already exists.

Certainly! Here is an updated version of the useCrudState hook that includes an error check in the create setter to ensure that an item with the same id does not already exist:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const selectors = {
    all: () => state,
    getById: id => state.find(item => item.id === id)
  };

  const setters = {
    create: item => {
      if (state.find(i => i.id === item.id)) {
        throw new Error(`Item with id ${item.id} already exists`);
      }
      setState([...state, item]);
    },
    remove: id => {
      setState(state.filter(item => item.id !== id));
    },
    update: (id, update) => {
      setState(
        state.map(item => (item.id === id ? { ...item, ...update } : item))
      );
    }
  };

  return [selectors, setters];
};

This hook works just like the previous versions, but it includes an error check in the create setter to ensure that an item with the same id does not already exist. If an item with the same id already exists, the create setter will throw an error.

I hope this helps! Let me know if you have any questions.

Q9: Now do the same with remove but check if the item is not preset.

Certainly! Here is an updated version of the useCrudState hook that includes an error check in the remove setter to ensure that the item to be removed exists:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const selectors = {
    all: () => state,
    getById: id => state.find(item => item.id === id)
  };

  const setters = {
    create: item => {
      if (state.find(i => i.id === item.id)) {
        throw new Error(`Item with id ${item.id} already exists`);
      }
      setState([...state, item]);
    },
    remove: id => {
      if (!state.find(i => i.id === id)) {
        throw new Error(`Item with id ${id} not found`);
      }
      setState(state.filter(item => item.id !== id));
    },
    update: (id, update) => {
      setState(
        state.map(item => (item.id === id ? { ...item, ...update } : item))
      );
    }
  };

  return [selectors, setters];
};

This hook works just like the previous versions, but it includes an error check in the remove setter to ensure that the item to be removed exists. If the item does not exist, the remove setter will throw an error.

I hope this helps! Let me know if you have any questions.

Q10: Create a hasWithId pure function and use it where ever an existence check is required.

Certainly! Here is an updated version of the useCrudState hook that includes a hasWithId pure function for checking if an item with a given id exists in the state, and that uses this function wherever an existence check is required:

import { useState } from 'react';

const useCrudState = initialState => {
  const [state, setState] = useState(initialState);

  const hasWithId = id => state.some(item => item.id === id);

  const selectors = {
    all: () => state,
    getById: id => state.find(item => item.id === id)
  };

  const setters = {
    create: item => {
      if (hasWithId(item.id)) {
        throw new Error(`Item with id ${item.id} already exists`);
      }
      setState([...state, item]);
    },
    remove: id => {
      if (!hasWithId(id)) {
        throw new Error(`Item with id ${id} not found`);
      }
      setState(state.filter(item => item.id !== id));
    },
    update: (id, update) => {
      setState(
        state.map(item => (item.id === id ? { ...item, ...update } : item))
      );
    }
  };

  return [selectors, setters];
};

This hook works just like the previous versions, but it includes a hasWithId function for checking if an item with a given id exists in the state, and it uses this function wherever an existence check is required.

I hope this helps! Let me know if you have any questions.

Q11: Write a short paragraph describing the useCrudState hook and a 2 point bullet list with it's features: CRUD operations, Error handling. Make an analogy to the React useState. Be succinct.

The useCrudState hook is a custom React hook that provides state management functionality similar to the built-in useState hook, but with the added ability to perform CRUD (create, read, update, delete) operations on the state. It exposes selectors for accessing the items in the state and setters for performing CRUD operations on the state. It also includes error handling to ensure that the setters are used correctly.

Features:

  • CRUD operations: create, remove, update setters for adding, deleting, and updating items in the state
  • Error handling: checks to ensure that items being added or deleted actually exist, and throws errors if they do not

The useCrudState hook is an useful tool for managing state in React components that require more advanced state management capabilities than those provided by the built-in useState hook.

Q12: Write an example of the hooks functionalities using an initial state of 3 todo items.

Certainly! Here is an example of how you might use the useCrudState hook to manage a state of todo items in a React component:

import { useCrudState } from './useCrudState';

function TodoList() {
  const initialState = [
    { id: 1, text: 'Take out the trash', completed: false },
    { id: 2, text: 'Do the dishes', completed: false },
    { id: 3, text: 'Buy milk', completed: true }
  ];

  const [selectors, setters] = useCrudState(initialState);

  const handleAddTodo = e => {
    e.preventDefault();
    const text = e.target.elements.todoText.value;
    setters.create({ id: Date.now(), text, completed: false });
  };

  const handleDeleteTodo = id => {
    setters.remove(id);
  };

  const handleToggleTodo = id => {
    const todo = selectors.getById(id);
    setters.update(id, { completed: !todo.completed });
  };

  return (
    <div>
      <form onSubmit={handleAddTodo}>
        <input type="text" name="todoText" />
        <button type="submit">Add Todo</button>
      </form>
      <ul>
        {selectors.all().map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodo(todo.id)}
            />
            {todo.text}
            <button onClick={() => handleDeleteTodo(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment