Debug School

rakesh kumar
rakesh kumar

Posted on

Crud using redux state management store in React and node js

Save a question in database

Step 1: create a button

      <h5 class="card-title mb-6">Question</h5>
        <div className="serch">
         <button
            className="btn btn-block add-btn"
            type="submit"
            onClick={() => dispatch(openQuestionModal())}
          >
            + Add Question
          </button>
      </div>
Enter fullscreen mode Exit fullscreen mode

step2
Using a dispatch function call a state management system redux store

  const dispatch = useDispatch()
Enter fullscreen mode Exit fullscreen mode

step3
perform the action creator function
the action creator openQuestionModal used to dispatch the OPEN_QUESTION_MODAL action is processed by the relevant reducer

export const openQuestionModal = () => {
  return (dispatch) => {
    dispatch({ type: OPEN_QUESTION_MODAL })
  }
}
Enter fullscreen mode Exit fullscreen mode

step4
state is being updated based on the action type

switch (action.type) {
    case OPEN_QUESTION_MODAL:
      return { ...state, isAddQuestionModalOpen: true }
Enter fullscreen mode Exit fullscreen mode
const initialState = {  
  isAddQuestionModalOpen: false,  
}
Enter fullscreen mode Exit fullscreen mode

step5
Controlling the visibility of the modal through a state variable

  <Modal
        show={isAddQuestionModalOpen}
        onHide={() => dispatch(closeQuestionModal())}
        aria-labelledby="contained-modal-title-vcenter"
        centered
      >
Enter fullscreen mode Exit fullscreen mode

step6
show a popup modal form that contain textfield dropdown and button

 <Modal.Header closeButton>
          <Modal.Title>
            {"add question"}
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <FormRow
            type={"text"}
            value={values.question}
            name="question"
            handleChange={handleChange}
            labelText={"question"}
            required={true}
          />
          <FormRowSelect
            labelText={"type"}
            name="type"
            value={values.type}
            handleChange={handleChange}
            list={["subjective", "multiple"]}
          />

          {values.type === "multiple" && (
            <button className="btn" onClick={hanndleAddOptions}>
              add options
            </button>
          )}
          {values.type === "multiple" &&
            options.map((i) => {
              return (
                <div>
                  <FormRow
                    name={`${i}`}
                    value={values.options[i]}
                    handleChange={handleOptionChange}
                    labelText={`option ${i + 1}`}
                  />
                </div>
              )
            })}
          <button className="btn" onClick={handleSubmit}>
            submit
          </button>
          <button
            className="btn"
            onClick={() => dispatch(closeQuestionModal())}
          >
            cancel
          </button>
        </Modal.Body>
      </Modal>
Enter fullscreen mode Exit fullscreen mode

code explanation

how to use dropdown

  <FormRowSelect
            labelText={"type"}
            name="type"
            value={values.type}
            handleChange={handleChange}
            list={["subjective", "multiple"]}
          />
Enter fullscreen mode Exit fullscreen mode
const initialState = {
  question: "",
  type: "subjective",
  options: [],
}
Enter fullscreen mode Exit fullscreen mode

how to dynamically sets name,value and labelText using button

{values.type === "multiple" && (
            <button className="btn" onClick={hanndleAddOptions}>
              add options
            </button>
          )}
          {values.type === "multiple" &&
            options.map((i) => {
              return (
                <div>
                  <FormRow
                    name={`${i}`}
                    value={values.options[i]}
                    handleChange={handleOptionChange}
                    labelText={`option ${i + 1}`}
                  />
                </div>
              )
            })}
Enter fullscreen mode Exit fullscreen mode

When u select dropdown and type multiple

{values.type === "multiple" && (
            <button className="btn" onClick={hanndleAddOptions}>
              add options
            </button>
          )}
Enter fullscreen mode Exit fullscreen mode

When we click add options then call hanndleAddOptions function

const [options, setOptions] = useState([0])
const hanndleAddOptions = () => {
const last = options[options.length - 1]//options.length=1==>(1-1)==>0
    setOptions([...options, last + 1])
  }
Enter fullscreen mode Exit fullscreen mode

This function hanndleAddOptions is used to add new options to the options state array.
[0]==>[0,1]==>[0,1,2]==>[0,1,2,3]...etc

maps over the options state array

options.map((i) => { ... }): This maps over the options state array and renders a component for each option index i using conditional rendering statement

{values.type === "multiple" &&
            options.map((i) => {
              return (
                <div>
                  <FormRow
                    name={`${i}`}
                    value={values.options[i]}
                    handleChange={handleOptionChange}
                    labelText={`option ${i + 1}`}
                  />
                </div>
              )
            })}
Enter fullscreen mode Exit fullscreen mode

How to update value of particular initial state object property options is array type using event handler function handleOptionChange

  const handleOptionChange = (e) => {
    const position = parseInt(e.target.name)
    const str = e.target.value
    const arr = values.options
    arr[position] = str//single value option1 or option2
    setValues({ ...values, options: arr })
  }
Enter fullscreen mode Exit fullscreen mode

output

setValues({ ...values, options: arr })// in arr all arr[0],arr[1],arr[2]
 options: ["Option1", "Option2", "Option3"],
Enter fullscreen mode Exit fullscreen mode

Step7:after filling form submit the data
dispatches an action createQuestion action creator using the dispatch function

 <button className="btn" onClick={handleSubmit}>
            submit
  </button>

 const handleSubmit = async (e) => {
    e.preventDefault()
    console.log(values)
    dispatch(createQuestion(values))
    setValues(initialState)
  }
Enter fullscreen mode Exit fullscreen mode

step9
perform the action creator function
This action creator is used in a Redux-based application to asynchronously create a new question and update the state based on the response received from the server

How to update state(apply if condition) inside action creator based on the response received from the server

How different actions are dispatched based on response received from the server

export const createQuestion = (values) => {
  return async (dispatch) => {
    dispatch({ type: CREATE_QUESTION_BEGIN })
    try {
      const response = await authFetch.post("/question", values)
      if (response.data.newQuestion.type === "subjective") {
        dispatch({
          type: CREATE_QUESTION_SUBJECTIVE_SUCCESS,
          payload: { question: response.data.newQuestion },
        })
      }
      if (response.data.newQuestion.type === "multiple") {
        dispatch({
          type: CREATE_QUESTION_MULTIPLE_SUCCESS,
          payload: { question: response.data.newQuestion },
        })
      }
      toast.success("question created successfully")
    } catch (error) {
      dispatch({ type: CREATE_QUESTION_ERROR })
      console.log(error)
      toast.error("something went wrong while creating question")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

in controller

const createQuestion = async (req, res) => {
  const { question, type } = req.body
  if (!question) {
    throw new BadRequestError("provide question")
  }
  const newQuestion = await Question.create(req.body)
  res.status(201).json({ newQuestion })
}
Enter fullscreen mode Exit fullscreen mode

Handle the state updates in response to two different success actions

case CREATE_QUESTION_SUBJECTIVE_SUCCESS:
      return {
        ...state,
        isAddQuestionModalOpen: false,
        editingQuestion: null,
        isEditingQuestion: false,
        subjective: [...state.subjective, action.payload.question],
      }
    case CREATE_QUESTION_MULTIPLE_SUCCESS:
      return {
        ...state,
        isAddQuestionModalOpen: false,
        editingQuestion: null,
        isEditingQuestion: false,
        multiple: [...state.multiple, action.payload.question],
      }
Enter fullscreen mode Exit fullscreen mode

Output

Image description

READ OR DISPLAY OR RENDER DATA IN REACT JS

Image description

  <Tabs
          activeTab="1"
          className="tabss mb-3"
          ulClassName=""
          activityClassName="bg-success"
          // onClick={(event, tab) => console.log(event, tab)}
        >
          <Tab title="Subjective" className="mr-3">
            <Subjective />
          </Tab>
          <Tab title="Multiple" className="mr-3">
            <Multiple />
          </Tab>
        </Tabs>
Enter fullscreen mode Exit fullscreen mode
import Subjective from "./Subjective"
import Multiple from "./Multiple"
Enter fullscreen mode Exit fullscreen mode

Subjective.js
Select specific data from the Redux store's state using useSelector

const dispatch = useDispatch()
  const { subjective } = useSelector((state) => state.feedback)
Enter fullscreen mode Exit fullscreen mode

In redux store

   case GET_ALL_QUESTION_SUBJECTIVE_SUCCESS:
      return { ...state, subjective: action.payload.questions }
Enter fullscreen mode Exit fullscreen mode

Image description
In controller

How to get different data based on different route query parameter in same function controller

const getAllQuestion = async (req, res) => {
  const { subjective, multiple } = req.query
  let questions
  if (subjective) {
    questions = await Question.find({ type: "subjective" })
  }
  if (multiple) {
    questions = await Question.find({ type: "multiple" })
  }
  res.status(200).json({ questions })
}
Enter fullscreen mode Exit fullscreen mode

Image description

How to dispatch different action based on different boolean value parameter in action creator function getAllQuestion

export const getAllQuestion = (values) => {
  return async (dispatch) => {
    const { subjective, multiple } = values
    dispatch({ type: GET_ALL_QUESTION_BEGIN })
    try {
      let response
      if (subjective) {
        response = await authFetch.get(`/question?subjective=true`)
        dispatch({
          type: GET_ALL_QUESTION_SUBJECTIVE_SUCCESS,
          payload: { questions: response.data.questions },
        })
      }
      if (multiple) {
        response = await authFetch.get(`/question?multiple=true`)
        dispatch({
          type: GET_ALL_QUESTION_MULTIPLE_SUCCESS,
          payload: { questions: response.data.questions },
        })
      }

      // console.log(response)
    } catch (error) {
      dispatch({ type: GET_ALL_QUESTION_ERROR })
      toast.error("error getting questions")
      console.log(error)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
 if (subjective) {
      dispatch(
        getAllQuestion({
          subjective: true,
          startDate: values.startDate,
          endDate: values.endDate,
        })
      )
    }
Enter fullscreen mode Exit fullscreen mode

In subjective.js

Image description
after apply filter display the data
add edit and delete in every subjective question

return (
    <Wrapper>
      <QuestionFilter subjective={true} />
      {subjective.length > 0 &&
        subjective.map((i) => {
          return (
            <div>
              <p>
                <ApiIcon fontSize="15px" style={{ marginRight: 
         "10px" }} />
                {capitalizeFirstLetter(i.question)}
                <DriveFileRenameOutlineSharpIcon
                  className="edit"
                  onClick={() => dispatch(setEditSubjective(i._id))}
                />
                <DeleteIcon
                  className="del"
                  onClick={() => dispatch(deleteSingleQuestion(i._id))}
                />
              </p>
            </div>
          )
        })}
      <section></section>
    </Wrapper>
  )
}
Enter fullscreen mode Exit fullscreen mode

Image description

Image description

In multiple.js

Select specific data from the Redux store's state using useSelector

  const dispatch = useDispatch()
  const { multiple } = useSelector((state) => state.feedback)
Enter fullscreen mode Exit fullscreen mode

In redux store

    case GET_ALL_QUESTION_MULTIPLE_SUCCESS:
      return { ...state, multiple: action.payload.questions }
Enter fullscreen mode Exit fullscreen mode

Image description

Image description
after apply filter display the data
add edit and delete in every multiple question

  <Wrapper>
      <QuestionFilter multiple={true} />
      {multiple.length > 0 &&
        multiple.map((i) => {
          return (
            <div>
              <h6>
                <ApiIcon fontSize="15px" style={{ marginRight: "10px" }} />
                {capitalizeFirstLetter(i.question)}
                <DriveFileRenameOutlineSharpIcon
                  className="edit"
                  onClick={() => dispatch(setEditMultiple(i._id))}
                />
                <DeleteIcon className="del" />
              </h6>
              <div className="options">
                {i.options.map((o, i) => {
                  return (
                    <p key={i}>
                      <FiberManualRecordRoundedIcon
                        style={{ marginRight: "3px", fontSize: "5px" }}
                      />{" "}
                      {o}
                    </p>
                  )
                })}
              </div>
            </div>
          )
        })}
      <section></section>
    </Wrapper>
Enter fullscreen mode Exit fullscreen mode

How to get data nested array
Image description

Image description

Output

Image description

Edit and Update question

Image description
In subjective.js
step1: create a button
Image description

Step2:Using a dispatch function call a state management system redux store

Image description
step3
perform the action creator function
the action creator setEditSubjective used to dispatch the SET_EDIT_SUBJECTIVE action is processed by the relevant reducer

export const setEditSubjective = (id) => {
  return (dispatch) => {
    dispatch({ type: SET_EDIT_SUBJECTIVE, payload: { id } })
  }
}
Enter fullscreen mode Exit fullscreen mode

above line defines line defines the setEditSubjective action creator function, which takes an id as its argument.
the dispatched action has a type of SET_EDIT_SUBJECTIVE and a payload containing the id of the
note: export function is used so that outside of this function anyone can access it

step4
state is being updated based on the action type

How to use find method to get data on the state.subjective array to search for a subjective question that matches the id

 case SET_EDIT_SUBJECTIVE:
      const question = state.subjective.find((i) => i._id === action.payload.id)
      return {
        ...state,
        editingQuestion: question,
        isSubjectiveModalOpen: true,
        isEditingQuestion: true,
      }
Enter fullscreen mode Exit fullscreen mode
const initialState = {
  loading: false,
  questions: [],
  subjective: [],
  multiple: [],
  isAddQuestionModalOpen: false,
  isSubjectiveModalOpen: false,
  isMultipleModalOpen: false,
  editingQuestion: null,
  isEditingQuestion: false,
}
Enter fullscreen mode Exit fullscreen mode

step5
Controlling the visibility of the modal through a state variable

 useEffect(() => {
    !editingQuestion &&
      dispatch(getSubjectiveQuestion()).then((data) => {
        setQuestion(data)
      })

    if (editingQuestion) {
      console.log(editingQuestion)
      setQuestion(editingQuestion)
    }
  }, [editingQuestion])
Enter fullscreen mode Exit fullscreen mode

Image description

step6
show a popup modal form that contain textfield

Image description

step7: Update data when u submit using event handler function

  const handleSubmit = async (e) => {
    e.preventDefault()
    // console.log(values)
    if (isEditingQuestion) {
      dispatch(
        updateQuestion({
          question: values.answer,
          type: editingQuestion.type,
          options: [],
          _id: editingQuestion._id,
        })
      )
    } else {
      dispatch(
        createFeedback({ answer: values.answer, question: question._id })
      )
    }
    setValues(initialState)
  }
Enter fullscreen mode Exit fullscreen mode

Image description

step8
perform the action creator function
This action creator is used in a Redux-based application to asynchronously create a new question and update the state based on the response received from the server
How to update state(apply if condition) inside action creator based on the response received from the server while updating

How different actions are dispatched based on response received from the server while updating

export const updateQuestion = (values) => {
  return async (dispatch) => {
    dispatch({ type: UPDATE_QUESTION_BEGIN })
    try {
      const { _id } = values
      console.log(values)
      const response = await authFetch.patch(`/question/${_id}`, values)
      console.log(response)
      if (response.data.updatedQuestion.type === "subjective") {
        dispatch({
          type: UPDATE_SUBJECTIVE_QUESTION_SUCCESS,
          payload: { updatedQuestion: response.data.updatedQuestion },
        })
      } else {
        dispatch({
          type: UPDATE_MULTIPLE_QUESTION_SUCCESS,
          payload: { updatedQuestion: response.data.updatedQuestion },
        })
      }
    } catch (error) {
      console.log(error)
      dispatch({ type: UPDATE_QUESTION_ERROR })
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

in controller function

const updateQuestion = async (req, res) => {
  // get question and array of strings from req.body
  const { id } = req.params
  if (!id) {
    throw new BadRequestError("please provide id")
  }
  const { question } = req.body
  if (!question) {
    throw new BadRequestError("please provide question")
  }
  const alreadyExist = await Question.findById(id)
  if (!alreadyExist) {
    throw new NotFoundError("no question with this id")
  }
  const updatedQuestion = await Question.findByIdAndUpdate(id, req.body, {
    new: true,
  })
  res.status(200).json({ updatedQuestion })
}
Enter fullscreen mode Exit fullscreen mode

In multiple.js
step1: create a button

Image description

Step2:Using a dispatch function call a state management system redux store

import { getAllQuestion, setEditMultiple } from "../../redux"
Enter fullscreen mode Exit fullscreen mode

step3
perform the action creator function
the action creator setEditMultiple used to dispatch the SET_EDIT_SUBJECTIVE action is processed by the relevant reducer

export const setEditMultiple = (id) => {
  return (dispatch) => {
    dispatch({ type: SET_EDIT_MULTIPLE, payload: { id } })
  }
}

Enter fullscreen mode Exit fullscreen mode

above line defines line defines the setEditMultiple action creator function, which takes an id as its argument.
the dispatched action has a type of SET_EDIT_MULTIPLE and a payload containing the id of the
note: export function is used so that outside of this function anyone can access it

step4
state is being updated based on the action type

How to use find method to get data on the state.subjective array to search for a subjective question that matches the id

  case SET_EDIT_MULTIPLE:
      const multipleQuestion = state.multiple.find(
        (i) => i._id === action.payload.id
      )
      return {
        ...state,
        editingQuestion: multipleQuestion,
        isMultipleModalOpen: true,
        isEditingQuestion: true,
      }
Enter fullscreen mode Exit fullscreen mode
const initialState = {
  loading: false,
  questions: [],
  subjective: [],
  multiple: [],
  isAddQuestionModalOpen: false,
  isSubjectiveModalOpen: false,
  isMultipleModalOpen: false,
  editingQuestion: null,
  isEditingQuestion: false,
}
Enter fullscreen mode Exit fullscreen mode

step5
Controlling the visibility of the modal through a state variable

useEffect(() => {
    !editingQuestion &&
      dispatch(getMultipleQuestion()).then((data) => {
        console.log(data)
        setQuestion(data)
      })

    if (editingQuestion) {
      setQuestion(editingQuestion)
    }
  }, [editingQuestion])
Enter fullscreen mode Exit fullscreen mode

Image description

step6
show a popup modal form that contain textfield

Image description

 const handleAddOptions = () => {
    const length = question.options.length
    console.log(length)
    // setOptions([...options, last + 1])
    const arr = question.options
    arr[length] = ""
    setQuestion({ ...question, options: arr })
  }
Enter fullscreen mode Exit fullscreen mode
  const handleChange = (i) => {
    const str = i.target.value.slice(0, -1)
    const position = question.options.indexOf(str)
    const arr = question.options
    arr[position] = i.target.value
    setQuestion({ ...question, options: arr })
  }
Enter fullscreen mode Exit fullscreen mode

step7: Update data when u submit using event handler function

const handleUpdate = () => {
    console.log(question)
    dispatch(updateQuestion(question))
  }
Enter fullscreen mode Exit fullscreen mode

step8
perform the action creator function
This action creator is used in a Redux-based application to asynchronously create a new question and update the state based on the response received from the server
How to update state(apply if condition) inside action creator based on the response received from the server while updating

How different actions are dispatched based on response received from the server while updating

export const updateQuestion = (values) => {
  return async (dispatch) => {
    dispatch({ type: UPDATE_QUESTION_BEGIN })
    try {
      const { _id } = values
      console.log(values)
      const response = await authFetch.patch(`/question/${_id}`, values)
      console.log(response)
      if (response.data.updatedQuestion.type === "subjective") {
        dispatch({
          type: UPDATE_SUBJECTIVE_QUESTION_SUCCESS,
          payload: { updatedQuestion: response.data.updatedQuestion },
        })
      } else {
        dispatch({
          type: UPDATE_MULTIPLE_QUESTION_SUCCESS,
          payload: { updatedQuestion: response.data.updatedQuestion },
        })
      }
    } catch (error) {
      console.log(error)
      dispatch({ type: UPDATE_QUESTION_ERROR })
    }
  }
}



Enter fullscreen mode Exit fullscreen mode

in controller function

const updateQuestion = async (req, res) => {
  // get question and array of strings from req.body
  const { id } = req.params
  if (!id) {
    throw new BadRequestError("please provide id")
  }
  const { question } = req.body
  if (!question) {
    throw new BadRequestError("please provide question")
  }
  const alreadyExist = await Question.findById(id)
  if (!alreadyExist) {
    throw new NotFoundError("no question with this id")
  }
  const updatedQuestion = await Question.findByIdAndUpdate(id, req.body, {
    new: true,
  })
  res.status(200).json({ updatedQuestion })
}
Enter fullscreen mode Exit fullscreen mode

HOW to delete question

Image description
In subjective.js
step1: create a delete icon
Image description
Step2:Using a dispatch function call a state management system redux store

import {
  getAllQuestion,
  setEditSubjective,
  deleteSingleQuestion,
} from "../../redux"
Enter fullscreen mode Exit fullscreen mode

step3
perform the action creator function
the action creator deleteSingleQuestion used to dispatch the DELETE_QUESTION_BEGIN action is processed by the relevant reducer

export const deleteSingleQuestion = (id) => {
  return async (dispatch) => {
    dispatch({ type: DELETE_QUESTION_BEGIN })
    try {
      const response = await authFetch.delete(`/question/${id}`)
      if (response.data.deletedQuestion.type === "subjective") {
        dispatch({
          type: DELETE_SUBJECTIVE_QUESTION_SUCCESS,
          payload: { deletedQuestion: response.data.deletedQuestion },
        })
      } else {
        dispatch({
          type: DELETE_MULTIPLE_QUESTION_SUCCESS,
          payload: { deletedQuestion: response.data.deletedQuestion },
        })
      }

      toast.success("question delete successfully")
    } catch (error) {
      console.log(error)
      dispatch({ type: DELETE_QUESTION_ERROR })
      toast.error("error deleting question")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
 case DELETE_SUBJECTIVE_QUESTION_SUCCESS:
      const filterSub = state.subjective.filter(
        (i) => i._id !== action.payload.deletedQuestion._id
      )
Enter fullscreen mode Exit fullscreen mode

in controller function

const deleteQuestion = async (req, res) => {
  const { id } = req.params
  if (!id) {
    throw new BadRequestError("please provide id")
  }

  const question = await Question.findById(id)
  if (!question) {
    throw new NotFoundError("no question with this id")
  }
  const deletedQuestion = await Question.findByIdAndDelete(id)
  res.status(200).json({ deletedQuestion })
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)