Skip to content

JDGonzal/react_redux_CRUD

Repository files navigation

react redux CRUD

Course based on Youtube video with Fazt Code

React y Redux Toolkit CRUD (con TailwindCSS)

0a. Preconditions

  1. Install the NPM and NODEJS in your system Nodejs Download
  2. Check in $path or %path% the nodeJS and npm are on it
C:/Program Files/nodejs
  1. Install also pnpm pnpm installation, it is more fast than npm
  2. Install Postman Postman Download
  3. Install MySQL 5.6.x MySQL Download 5.6.26
  4. Install Visual Studio Code Visual Studio Download

0b. Starting the proyect

  1. I used Vite, the best way to start any front-end project, with Typescrypt and a lot of templates:
npm init vite@latest react_redux_CRUD --template react-ts
  1. Following the instruccions, install the applications based on the package.json file.
pnpm install

This is an example running with pnpm pnpm install 3. And run the application.

pnpm dev
  1. Some changes in the "vite.config.ts" file, based on Create react app vs Vite:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path' // pnpm install -D @types/node

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],
  },
})
  1. Install the pending elements showing as error in path
pnpm install -D @types/node

1a. Redux to store main data

  1. Install Redux Toolkik an React-Redux
pnpm install @reduxjs/toolkit react-redux

pnpm install Redux 2. Create a "redux" directory, and put there the "store.ts" file. 3. Write in this file this first line:

import { configureStore } from "@reduxjs/toolkit";
  1. the next lines could be:
const store = configureStore({});
export default store;

or

export default configureStore({});

I used the second one.

  1. In the main file "App.tsx" add a Provider to include all the others
import { Provider } from "react-redux";
  1. Call the store from ""store.ts".
import store from "./redux/store";
  1. Finally I change the return by this:
    <>
      <Provider store={store}>
        <div className="App">
          <h1>Hola Mundo</h1>
        </div>
      </Provider>
    </>
  1. Create a directory called "states" into redux, and create a file called "tasksSlides.ts".
  2. Create a basic Slice to store the values and process to do:
import { createSlice } from '@reduxjs/toolkit'
export const tasksSlice = createSlice({
  name: 'taskSlice',
  initialState: [], 
  reducers:{
  }
});
export default tasksSlice.reducer;
  1. This is the "index.ts" file for this "states" directory:
export * from './tasksSlices';
  1. This is how I use this slice into the "store.ts" file:
export default configureStore({
  reducer: {
    task: tasksSlice.reducer,
  }
});
  1. I create a "pages" directory and add a Component called "Home.tsx"
  2. The "App.tsx" file to change and show the new page called "Home".
    <>
      <Provider store={store}>
        <div className="App">
          <Home/>
        </div>
      </Provider>
    </>

1b. To use the values and call the task

  1. In the "Home.tsx" file import the useSelector from 'react-redux'
  2. Add a "models" directory with a "task.model.ts" file.
export interface TaskInterface{
  id: string;
  name: string;
}
  1. Assigned this to "tasksSlices.ts", to use in the InitalState, with an empty array of ths data from TaskInterface.
  2. Very important to assign types or Interface in the "store.ts" file.
export interface AppStore{
  tasks : TaskInterface[];
}
export default configureStore<AppStore>({
  reducer: {
    tasks: tasksSlice.reducer,
  }
});
  1. Into "Home.tsk" file call the useSelector, using this format:
const tasksState = useSelector<AppStore>(state => state.tasks);

To show in a console.log.

  1. Added two new basic Components, called: TaskForm, and TaskList.
  2. The new components are added to "Home.tsx" file.

2a. Basic elements in TaskList and TaskForm

  1. Complete the initialState in "tasksSlices.ts" file.
const initialState: TaskInterface[] = [
  { id: "1", title: "Task1", description: "Task1 description", completed:false,},
  { id: "2", title: "Task2", description: "Task2 description", completed:false,}];
  1. Then I have to chenge the TaskInterface in "task.model.ts" file:
export interface TaskInterface{
  id: string;
  title: string;
  description: string;
  completed: boolean;
}
  1. Remove or become to Comment the useSelector in "Home.tsx" file.
  2. Put the useSelector in the "TaskList.tsx" file:
const tasksState = useSelector((state:AppStore) => state.tasks );
  1. Use to show the list of elements
      {tasksState.map(task =>(
        <div key={task.id}>
          <h3>{task.title}</h3>
          <p>{task.description}</p>
        </div>
      ))} 
  1. to "taskForm.tsk" component, add an <input>, <textarea>, and a <button>.
  2. Add an useState just with an empty title, and description:
  const [task, setTask] = useState({ title:'', description: '' });
  1. put an Event manager when Change, and assign to the <input>, and <textarea>
  const handleChange = (e:any) => {
    console.log('event:', e.target.value, e.target.name);
  };
  1. The handleChange method change to store
  2. For the <button> I associate and event called handleSubmit
  const handleSubmit =  (e:any) => {
    e.preventDefault(); // Avoid to Page refresing
    console.log(task);
  }

2b. List and Create (react RTK)

  1. In the "tasksSlices.ts" file add finale a first function, into reducers:{.
  reducers: {
    addTask: (state, action) =>{
      console.log(state, action);
    }
  1. Add to "TaskForm.tsx" and useDispatch()
const dispatch=useDispatch();
  1. The handleSubmit to add this dispatch.
    dispatch(addTask(task));
  1. Correction to addTask of the "taslSlice.ts" file, to really add an object.
    addTask: (state, action) =>{
      state.push(action.payload);
    }
  1. There are and error, because the "id" is empty when it shows in "TaskList", then install a Generator of ID:
pnpm i uuid
  1. As well install the types for this uuid:
npm i --save-dev @types/uuid
  1. Add the new generator of uuid in the "TaskForm.tsx" file.
import { v4 as uuid } from 'uuid';
  1. Use this uuid when you are going to use the dispatch(addTask(:
    dispatch(addTask({
      ...task,
      id: uuid(),
    }));

3. Delete a task (React RTK)

  1. Add a button in "TaskList.tsx" file by each Task:
    <button onClick={()=>handleDelete(task.id)}>Delete</button>
  1. By now the handleDelete method with a consol.log with the id
  const handleDelete = (id:string) => {
    console.log('event:', id);
  };
  1. Add a new reducers into "TaskSlice.ts" file:
    deleleteTask: (state, action) =>{
      console.log(action.payload);
    }

Note: I can not use delTask in the reducers

  1. Add in the export list:
export const {addTask, deleteTask} = tasksSlice.actions;
  1. in the "TaskList.tsx" add the useDispatch from 'reac-redux'.
const dispatch=useDispatch();
  1. Import the delTask from "../redux" directory
import { AppStore, deleteTask } from "../redux";
  1. Call the deleteTask in the handleDelete
  const dispatch=useDispatch();

  const handleDelete = (id: string) => {
    dispatch(deleteTask(id));
  };  
  1. Change the deleleteTask with the find an element in the array:
    const taskFound = state.find( task => task.id === action.payload);
  1. Finally with the Task Found, lets to delete of array with split, into a conditional:
      if (taskFound) {
        state.splice(state.indexOf(taskFound),1);
      }

4a. Routes react-router-dom@6 (React RTK)

  1. Install the react-router-dom
pnpm install react-router-dom
  1. Also remember you are working with TYPESCRIPT, then you need install @types/:
pnpm install @types/react-router-dom
  1. We are working in "Home.tsx" file, first deleting the "Home" message into <div>.
  2. Import the BrowserRouter, Routes, and Route from react-router-dom:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
  1. Add the the BrowserRouter, and Routes components are over the TaskList, and TaskForm:
      <BrowserRouter>
        <Routes>
          <TaskForm />
          <TaskList />
        </Routes>
      </BrowserRouter>
  1. Starting to use the Route, calling the TaskListas a root, and TaskForm in site '/create-task'.
        <Routes>
          <Route path='/' element={<TaskList/>} />
          <Route path='/create-task' element={<TaskForm/>} />
        </Routes>

4b. Improvement the routes in the pages

  1. Import in "TaskForm.tsx" the useNavigate from reac-router-dom.
  2. I'm going to instantiate as the useDispatch():
const navigate = useNavigate();
  1. After execute the dispatch , back to the original page.
  2. Add a "Navbar.tsx" component to link the pages, in "components" directory:
import { Link } from "react-router-dom";
function Navbar() {
  return (
    <nav className="navbar">
      <ul>
        <li> <Link to="/"> Home</Link>{" "} </li>
        <li> <Link to="/create-task"> Create Task</Link> </li>
      </ul>
    </nav>
  );
}
export default Navbar;
  1. Update the barrel or the "index.ts" from "components" directory.
export { default as Navbar } from './Navbar';
export { default as TaskForm } from './TaskForm';
export { default as TaskList } from './TaskList';
  1. Call the <Navbar/> below of <BrowserRouter>, in "Home.tsk" file.
  2. in "TaskList" can add a quantity of task as first element after <div>:
      <h1>Tasks: {tasksState.length}</h1>

Note: I add some elemenst in "App.css" file:

Horizontal Navigation Bar Examples

/* Elements of the "Navbar.tsx" file */
ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #333;
}
li {
  float: left;
}
li a {
  display: block;
  color: white;
  text-align: right;
  padding: 14px 16px;
  text-decoration: none;
}
/* Change the link color to #111 (black) on hover */
li a:hover {
  background-color: #111;
}

5. Update a task (React RTK)

  1. Add a Link in the "TaskList.tsk" component, below the Delete <button>.
      <Link to="">Update</Link>
  1. Add a route into "Home.tsx" file:
      <Route path={`/update-task/:id`} element={<TaskForm />} />
  1. Correction to the pending in Link of "TaskLst.tsx" file:
      <Link to={`/update-task/${task.id}`}>Update</Link>
  1. Verify in "TaskForm.tsx" file the ID with useParams first get params:
    const params   = useParams();
  1. Next into useEffect, show params in "TaskForm.tsx" file:
    useEffect(()=>{
      console.log(params);
    }, [params.id, tasks]); 
  1. With the useSelector we get the state or tasks list from AppStore:
    const tasks = useSelector((state: AppStore) => state.tasks);
  1. Replace the console.log of useEffect in "TaskForm.tsx" file, with a conditional:
      if (params.id){
        setTask(tasks.find(task => task.id === params.id));
      }
  1. Adding the value for each data to read later by screen: <input
          value={task.title}
  • and <textarea
          value={task.description}
  1. For handleSubmit, add a contitional , to know if is add or update in "TaskForm.tsx" file:
    if (params.id) {
      dispatch();
    } else {
      dispatch( addTask({ ...task, id: uuid(), }) );
    }
  1. Add an updateTask: (state, action) =>{ in "tasksSlices.ts" file.
    updateTask: (state, action) =>{
      const {id, title, description} = action.payload;
      const taskFound = state.find( task => task.id === id);
      if(taskFound) {
        taskFound.title = title;
        taskFound.description = description;
      }
    },
  1. Correction to the edit dispatch in "TaskForm.tsx" file:
    if (params.id) {
      dispatch( updateTask(task));

6a. Tailwind CSS Installation and configuration

  1. Go the Tailwind CSS, and select "Framework Guides" option.
  2. Becasue I used the "Vite", select "Vite".
  3. Run this command of the 2 step "Install Tailwind CSS" option, in a terminal:
pnpm install -D tailwindcss postcss autoprefixer

pnpm install -D tailwindcss

  1. Run this process to Initialize the tailwind css or create the config Tailwind file:
npx tailwindcss init -p
  1. Add the paths into the content: [], to all of your template files in your "tailwind.config.js" file.
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  1. Delete all code into "index.css" file.
  2. Add the @tailwind directives for each of Tailwind’s layers to the "index.css" file.
@tailwind base;
@tailwind components;
@tailwind utilities;

6b. Using the Tailwin CSS in the Application

  1. For fisrt Segment <>, become in a <div>and add a className:
<div className="bg-zinc-200 h-screen text-blue-900">
  1. Add another <div> before of <Navbar>, with a className:
<div className="flex items-center justify-center h-full">
  1. Add a another <div> element into "home.tsx" file, before of <Navbar>:
<div className="w-4/6">
  1. Delete all from "App.css" file.
  2. Add to <nav> element a className:
<nav className="flex justify-end items-center py-4">
  1. Add a className to <h1> element:
<h1 className="flex justify-between items-center py-4">
  1. Add a className into "Navbar.tsx" for the <Link> element:
className="text-neutral-500 hover:text-neutral-700"
  1. The "TaskList" to put in a grid, first arround all the tasks.map add a <div> element with a className:
<div className="grid grid-cols-3 gap-3">
  1. Put a each element a className, to create a cards .
<div key={task.id} className="bg-neutral-500 text-neutral-100 p-1 rounded-md">
  1. Move in "TaskList.tsx" <button> and <Link> below the <h3>{task.title}</h3> and closed in a <div> with a class:
              <h3>{task.title}</h3>
              <div className="flex gap-x-2">
                <button onClick={() => handleDelete(task.id)} className="btn bg-red-800 hover:bg-red-600 text-xs rounded-md px-2 py-1">Delete</button>
                <Link to={`/update-task/${task.id}`} className="btn bg-blue-800 hover:bg-blue-600 text-xs rounded-md px-2 py-1">Update</Link>
              </div>
  1. Put a <header> over the <h3> , and closing wiht the </div> of <button> and <Link>, with a classname in "TaskList.tsx" file:
            <header className="flex justify-between">
  1. For "TaskForm.tsx" add className in <form element.
<form onSubmit={handleSubmit} className="bg-neutral-500 text-neutral-100 rounded-md max-w-sm p-4">
  1. Add a Label before the <input element:
<label htmlFor="title" className="block text-xs font-bold mb-1">Task:</label>
  1. Add a Label before the <textarea element:
<label htmlFor="description" className="block text-xs font-bold mb-1 mt-3">Description:</label>
  1. Add a className to the <input element:
className="w-full p-2 rounded-md bg-zinc-700"
  1. Add the className to the <textarea element:
className="w-full p-2 rounded-md bg-zinc-700"
  1. Complete the "Save" <button adding a className :
<button className="btn bg-green-800 hover:bg-green-600 text-xs rounded-md px-2 py-1">

Releases

No releases published

Packages

 
 
 

Contributors