>웹 프론트엔드 >JS 튜토리얼 >Perseid와 React를 사용하여 사용자 피드백 양식 작성

Perseid와 React를 사용하여 사용자 피드백 양식 작성

DDD
DDD원래의
2024-09-19 03:45:061010검색

이 가이드에서는 FormikReact Hook Form의 강력한 대안인 @perseid/form 라이브러리를 사용하여 동적 사용자 피드백 양식을 작성하는 과정을 안내합니다. @perseid/form을 사용하여 양식 상태, 유효성 검사 및 조건부 렌더링을 쉽게 관리하는 방법을 살펴보겠습니다. 우리가 만들 양식은 사용자에게 서비스를 평가하고 피드백을 제공하도록 요청합니다. 평가에 따라 "감사합니다" 메시지가 표시되거나 사용자에게 추가 피드백을 제공하라는 메시지가 표시됩니다.

? 시작해 보세요!


1단계: 양식 구성 설정

첫 번째 단계는 양식 구성을 정의하는 것입니다. 이 구성은 필드, 단계 및 이들 사이의 흐름을 포함하여 양식이 작동하는 방식을 간략하게 설명합니다. 여기서는 사용자 평가에 따른 조건부 논리를 사용하여 평가 및 리뷰를 위한 필드를 생성하겠습니다. 또한 긍정적인 피드백과 부정적인 피드백에 대한 메시지를 정의할 것입니다.

구성 코드는 다음과 같습니다.

import { type Configuration } from "@perseid/form";

const formConfiguration: Configuration = {
  // Root step-the form will start from there.
  root: "feedback",
  // Callback triggered on form submission.
  onSubmit(data) {
    alert(`Submitting the following JSON: ${JSON.stringify(data)}`);
    return Promise.resolve();
  },
  // `fields` define the data model the form is going to deal with.
  // Expect the submitted data JSON to match this schema.
  fields: {
    rating: {
      type: "integer",
      required: true,
    },
    review: {
      type: "string",
      required: true,
      // Display this field only if condition is met...
      condition: (inputs) =>
        inputs.rating !== null && (inputs.rating as number) < 3,
    },
    // Type `null` means that the value of this field will not be included in submitted data.
    submit: {
      type: "null",
      submit: true,
    },
    message_good: {
      type: "null",
    },
    message_bad: {
      type: "null",
    },
  },
  // Now that fields are defined, you can organize them in a single or multiple steps,
  // depending on the UI you want to build!
  steps: {
    feedback: {
      fields: ["rating", "review", "submit"],
      // Whether to submit the form at the end of this step.
      submit: true,
      // Next step is conditionned to previous user inputs...
      nextStep: (inputs) =>
        (inputs.rating as number) < 3 ? "thanks_bad" : "thanks_good",
    },
    thanks_good: {
      fields: ["message_good"],
    },
    thanks_bad: {
      fields: ["message_bad"],
    },
  },
};

이 구성에서는:

  • 양식은 피드백 단계에서 시작됩니다.
  • 양식에는 평가(필수) 및 검토(평가가 3 미만인 경우 선택 사항)라는 두 개의 필드가 있습니다.
  • 평가에 따라 양식은 "좋음" 또는 "나쁨" 피드백 메시지로 이동합니다.
  • 양식을 제출하면 제출된 데이터에 대한 간단한 알림이 실행됩니다.

여기서 핵심적으로 파악해야 할 점은 필드 속성의 기능입니다. 이는 제출될 데이터의 구조를 정의하며 본질적으로 데이터 모델 역할을 합니다. 반면에 steps 속성은 양식의 흐름을 간략하게 설명하여 이러한 필드가 사용자에게 표시되는 방식을 결정합니다.


2단계: 양식의 React 구성 요소 만들기

이제 구성이 완료되었으므로 양식을 렌더링할 실제 UI를 구축할 차례입니다. @perseid/form/react를 사용하면 사용자 정의 필드 구성 요소를 만들어 양식의 각 부분에 대한 사용자 상호 작용을 관리할 수 있습니다.

핵심 React 구성요소는 다음과 같습니다.

import React from "react";
import Form, { type FormFieldProps } from "@perseid/form/react";

// The actual React component, used to build the UI!
function Field(props: FormFieldProps): JSX.Element {
  const { path, engine, value, status } = props;
  const [currentRating, setCurrentRating] = React.useState(0);

  // Display a different element depending on the field...

  if (path === "thanks_good.1.message_good") {
    return (
      <div className="message">
        <h1>Thanks for the feedback ?</h1>
        <p>We are glad you enjoyed!</p>
      </div>
    );
  }

  if (path === "thanks_bad.1.message_bad") {
    return (
      <div className="message">
        <h1>We're sorry to hear that ?</h1>
        <p>We'll do better next time, promise!</p>
      </div>
    );
  }

  if (path === "feedback.0.rating") {
    return (
      // Depending on the field status, define some extra classes for styling...
      <div
        className={`rating ${status === "error" ? "rating--error" : ""}`}
        onMouseLeave={() => {
          setCurrentRating((value as number | null) ?? 0);
        }}
      >
        <h1>How would you rate our service?</h1>
        {[1, 2, 3, 4, 5].map((rating) => (
          <span
            key={rating}
            className={`rating__star ${
              currentRating >= rating ? "rating__star--active" : ""
            }`}
            onMouseEnter={() => {
              setCurrentRating(rating);
            }}
            onClick={() => {
              // On click, notify the form engine about new user input.
              engine.userAction({ type: "input", path, data: rating });
            }}
          ></span>
        ))}
      </div>
    );
  }

  if (path === "feedback.0.review") {
    return (
      <div className={`review ${status === "error" ? "review--error" : ""}`}>
        <label>Could you tell us more?</label>
        <textarea
          onChange={(e) =>
            engine.userAction({ type: "input", path, data: e.target.value })
          }
        />
      </div>
    );
  }

  // path === 'feedback.0.submit'
  return (
    <button
      className="submit"
      onClick={() => {
        engine.userAction({ type: "input", path, data: true });
      }}
    >
      Submit
    </button>
  );
}

여기서 Field 구성 요소는 경로 소품을 사용하여 렌더링할 대상을 결정합니다.

  • 사용자가 별점을 선택할 수 있는 평가 구성요소입니다.
  • 사용자가 추가 피드백을 제공할 수 있는 텍스트 영역

평가에 따라 표시되는 '감사합니다' 메시지입니다. 양식은 사용자 입력에 따라 필드와 단계를 동적으로 조정합니다.

정말 멋지죠?

Building a User Feedback Form with Perseid and React


3단계: 애플리케이션 실행

이제 양식 구성과 구성 요소가 준비되었으므로 기본 React 앱에 통합해 보겠습니다. 양식을 초기화하고 렌더링하는 코드는 다음과 같습니다.

import { createRoot, type Root } from "react-dom/client";

// Let's run the app!
let app: Root;

// Creating React root...
const container = document.querySelector("#root") as unknown as HTMLElement;
app = createRoot(container);
app.render(
  // Router is the main component for any Perseid app.
  <Form Field={Field} configuration={formConfiguration} />
);

이 코드는 React의 createRoot API를 사용하여 DOM에 양식을 마운트합니다. 구성과 필드 구성 요소를 연결하는 양식 구성 요소가 다른 모든 것을 처리합니다.

4단계: 스타일 추가

알겠습니다. 앱 로직이 있지만 지금 코드를 실행해 보면 약간... 원시적이라는 것을 알 수 있습니다.

Building a User Feedback Form with Perseid and React

스타일과 애니메이션을 추가하여 양식을 멋지게 만들어 보겠습니다! 다음은 훨씬 더 매력적인 간단한 스타일시트입니다.

// A few animations for fun...

@keyframes swipe-out {
  0% {
    opacity: 1;
    transform: translateX(0);
  }
  75% {
    opacity: 0;
    transform: translateX(-100%);
  }
  100% {
    opacity: 0;
    transform: translateX(-100%);
  }
}

@keyframes swipe-in-one {
  0% {
    opacity: 0;
    transform: translateX(100%);
  }
  75% {
    transform: translateX(0);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes swipe-in-two {
  0% {
    opacity: 0;
    transform: translateX(0);
  }
  75% {
    transform: translateX(-100%);
  }
  100% {
    opacity: 1;
    transform: translateX(-100%);
  }
}

@keyframes bubble-in {
  0% {
    transform: scale(0.5);
  }
  75% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

// Some global basic styling...

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  display: grid;
  height: 100vh;
  color: #aaaaaa;
  align-items: center;
  font-family: "Helvetica", sans-serif;
}

// And form-specific styling.

.perseid-form {
  width: 100%;
  margin: auto;

  &__steps {
    display: flex;
    overflow: hidden;
  }

  &__step {
    min-width: 100%;
    padding: 1rem 3rem;
    animation: 500ms ease-in-out forwards swipe-out;

    &__fields {
      display: grid;
      row-gap: 2rem;
    }
  }

  &__step[class*="active"]:first-child {
    animation: 500ms ease-in-out forwards swipe-in-one;
  }
  &__step[class*="active"]:last-child:not(:first-child) {
    animation: 500ms ease-in-out forwards swipe-in-two;
  }
}

.submit {
  border: none;
  cursor: pointer;
  padding: 1rem 2rem;
  border-radius: 8px;
  color: #fefefe;
  font-size: 1.25rem;
  background: #46c0b0;
  justify-self: flex-end;
  transition: all 250ms ease-in-out;

  &:hover {
    background: #4cccbb;
  }
}

.rating {
  position: relative;
  padding: 0.25rem 0;

  &__star {
    cursor: pointer;
    display: inline-block;
    font-size: 2rem;
    min-width: 2rem;
    min-height: 2rem;

    &::after {
      content: "⚪️";
    }

    &--active {
      animation: 250ms ease-in-out forwards bubble-in;
      &::after {
        content: "?";
      }
    }
  }

  &[class*="error"] {
    &::after {
      left: 0;
      bottom: -1.5rem;
      color: #f13232;
      position: absolute;
      font-size: 0.75rem;
      content: "? This field is required";
      animation: 250ms ease-in-out forwards fade-in;
    }
  }
}

.review {
  display: grid;
  row-gap: 1rem;
  position: relative;
  animation: 250ms ease-in-out forwards fade-in;

  label {
    font-size: 1.25rem;
  }

  textarea {
    resize: none;
    min-height: 5rem;
    border-radius: 8px;
    border: 1px solid #46c0b0;
    transition: all 250ms ease-in-out;
  }

  &[class*="error"] {
    &::after {
      left: 0;
      bottom: -1.5rem;
      color: #f13232;
      position: absolute;
      font-size: 0.75rem;
      content: "? This field is required";
      animation: 250ms ease-in-out forwards fade-in;
    }
  }
}

@media screen and (min-width: 30rem) {
  .perseid-form {
    max-width: 30rem;
  }
}

자, 짜잔?


결론

축하합니다! ? Perseid와 React를 사용하여 동적 사용자 피드백 양식을 구축했습니다.

이 튜토리얼에서는 다음 방법을 살펴보았습니다.

  • 조건부 논리로 양식 구성을 정의합니다.
  • 사용자 상호작용을 처리하기 위한 맞춤형 React 구성요소를 구축하세요.
  • 앱에서 양식을 렌더링하고 애니메이션과 맞춤 CSS로 스타일을 지정하세요.

사용 사례에 맞게 추가 필드와 단계를 자유롭게 실험해 보세요. 멋진 양식을 재미있게 만들어 보세요! ?


  • ? 더 많은 예시
  • ✅ 전체 문서
  • ? 우리의 디스코드에 참여하세요
  • ? GitHub에서 프로젝트를 시작하세요.
  • ❤️ 페르세이드 후원

위 내용은 Perseid와 React를 사용하여 사용자 피드백 양식 작성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.