동적으로 제어되는 React 양식 만들기

9933 단어 reactformjavascript
동적 제어(DCF) 양식은 사용자가 버튼 클릭으로 입력 필드를 추가하거나 제거할 수 있는 양식입니다. 단일 입력 필드의 대안이며 절차적 또는 그룹화된 콘텐츠를 캡슐화하기 위한 것입니다.

이 자습서에서는 DCF를 사용하여 재료와 단계를 분리하는 간단한 레시피 양식을 만듭니다.

이 튜토리얼에서는 React를 사용하여 양식을 생성하는 방법을 다룰 것입니다.

구체적으로 다음을 수행합니다.
  • 상태를 사용하여 양식을 구성하십시오.
  • 레시피 속성에 대한 handleChange 함수 작성
  • 재료 및 단계에 대한 추가 및 삭제 기능 작성
  • 재료 및 단계에 대한 렌더링 입력 기능 작성
  • 전체 양식에 대해 handleSubmit 작성
  • 양식 렌더링

  • 상태에서 레시피 구성



    새 React 앱에서 양식에 대한 구성 요소를 만듭니다.
    touch AddRecipeForm.js
    생성자는 양식 데이터를 상태로 유지합니다. 레시피에 제목, 요약, 재료(이름 및 양) 및 단계가 있어야 합니다.

     constructor(){
            super()
            this.state={
                title:"",
            summary: "",
            ingredients: [
                {name: "", amount: ""}
            ],
            steps: []
            }
        }
    

    보시다시피 제목과 요약 정보도 보유하고 있습니다. 이 정보는 배열에 보관할 필요가 없으므로 둘 모두에 대해 하나의 handleChange 함수를 사용할 수 있습니다.

    제목 및 요약에 대한 변경 처리



    사용자가 레시피의 제목과 요약을 작성할 수 있도록 단일 handleChange 함수를 작성하십시오.

    
        handleChange = (event) => {
            this.setState({
                [event.target.name]: event.target.value
            })
        }
    

    단계 및 성분에 대한 변경 처리



    그런 다음 성분 이름, 양 및 단계 변경을 별도로 처리해야 합니다.

    성분 상태 변경의 경우 다른 기능의 성분 이름과 양을 매핑해야 합니다.

     handleIngredientNameChange = (e, ingredientIndex) => {
            let newIngredientName = e.target.value;
            this.setState((prev) => {
              return {
                ...prev,
                ingredients: prev.ingredients.map((ingredient, index) => {
                  if (index == ingredientIndex) {
                    return { ...ingredient, name: newIngredientName};
                  } 
                  return ingredient;
                }),
              };
            });
          };
    
          handleIngredientAmountChange = (e, ingredientIndex) => {
            let newIngredientAmount = e.target.value;
            this.setState((prev) => {
              return {
                ...prev,
                ingredients: prev.ingredients.map((ingredient, index) => {
                  if (index == ingredientIndex) {
                    return { ...ingredient, amount: newIngredientAmount};
                  } 
                  return ingredient;
                }),
              };
            });
          };
    

    단계 변경의 경우 단계를 통해 매핑하기만 하면 됩니다.

      handleStepChange = (e, stepIndex) => {
            let newStep = e.target.value;
            this.setState((prev) => {
              return {
                ...prev,
                steps: prev.steps.map((step, index) => {
                  if (index == stepIndex) {
                    return { ...step, step_summary: newStep};
                  } 
                  return step;
                }),
              };
            });
          };
    

    성분 추가 및 제거



    우리는 사용자에게 재료를 추가하고 제거할 수 있는 옵션을 제공하고자 합니다. 필터를 사용하여 성분을 제거합니다.

    addIngredientInputs = () => {
            this.setState((prev) => {
                return {
                  ...prev,
                  ingredients: [...prev.ingredients, { name: "", amount:"" }],
                };
              });
        }
        removeIngredientInput = (e, ingredientIndex) => {
          e.preventDefault()
    
          this.setState({
            ingredients: this.state.ingredients.filter((ingredient, removedIngredient) => removedIngredient !== ingredientIndex )
          })
        }
    

    렌더 재료



    마지막으로 재료 입력을 렌더링해야 합니다. 여기에서 약간의 부트스트랩 스타일을 사용했습니다.

    renderIngredientInputs = () => {
                 return this.state.ingredients.map((ingredient, index) => {
              return (
    
                    <div key={`name ${index}`} 
                    className="form-group">
    
                    <input className="mb-3"
                        value={this.state.ingredients[index].name}
                        onChange={(e) => this.handleIngredientNameChange(e, index)}
                        placeholder="Name"
                        name="name"
    
                    />
    
                    <input
                        value={this.state.ingredients[index].amount}
                        onChange={(e) => this.handleIngredientAmountChange(e, index)}
                        placeholder="Amount"
                        name="amount"
    
                    />
                    <br></br>
    
                    <Button variant="outline-secondary" onClick={(e)=>this.removeIngredientInput(e,index)}>{this.state.ingredients[index].name ? `Delete ${this.state.ingredients[index].name}` : `Delete Ingredient`}</Button>
    
                </div>
              );
            });
          };
    

    여기에서 렌더링된 각 성분에 인덱스를 할당합니다. onChange 이벤트를 렌더에 배치하고 필요한 경우 재료를 제거하는 버튼도 추가합니다.

    단계 추가 및 제거



    단계를 추가하고 제거하는 것은 조금 더 간단하지만 동일한 논리를 따릅니다.

    
    
        addStepInputs = () => {
            this.setState((prev) => {
              return {
                ...prev,
                steps: [...prev.steps, ""],
              };
            });
          };
    
    removeStepInput = (e, stepIndex) => {
            e.preventDefault()
    
            this.setState({
              steps: this.state.steps.filter((step, removedStep) => removedStep !== stepIndex )
            })
          }
    

    재료와 마찬가지로 사용자에게 단계를 추가하거나 제거할 수 있는 옵션을 제공합니다. 그런 다음 단계 입력을 렌더링합니다.

    렌더링 단계 입력



    다시 말하지만, 저는 스타일링을 위해 약간의 부트스트랩을 사용했습니다. 여기서 고려해야 할 중요한 사항은 각 단계에 번호가 매겨져 있다는 것입니다. 단계가 추가되면 개수에 하나를 추가합니다. Step${index+1} 단계를 삭제하면 해당 단계가 삭제된 위치에 따라 개수가 변경됩니다. 인덱스가 0에서 시작하기 때문에 +1을 사용해야 합니다.

    renderStepInputs = () => {
                   }
            return this.state.steps.map((step, index) => {
              return (
                <div key={index} className="form-group">
              <fieldset>
                  <textarea
                    placeholder={`Step${index+1}`}
    
                    name="rec_steps"
                    id="textArea"
                    className="form-control"
                    onChange={(e) => this.handleStepChange(e, index)}
                    value={step.step_summary}
                  />
                  <button className="btn btn-secondary" type="button" onClick={(e)=>this.removeStepInput(e,index)}>{`Delete Step ${index+1}`}</button>
                  </fieldset>
                </div>
              );
            });
          };
          handleStepChange = (e, stepIndex) => {
            let newStep = e.target.value;
            this.setState((prev) => {
              return {
                ...prev,
                steps: prev.steps.map((step, index) => {
                  if (index == stepIndex) {
                    return { ...step, step_summary: newStep};
                  } 
                  return step;
                }),
              };
            });
          };
    
    
    

    작성 핸들 제출



    마지막으로, 데이터를 백엔드로 보내고 사용자를 재료 페이지로 되돌리는 handleSubmit 함수를 작성하십시오.

    handleSumbit = (e) => {
            e.preventDefault()
                this.props.onAddRecipe(this.state)
                this.props.history.push('/')
    
        }
    

    렌더 기능에 모두 합치기



    렌더링 기능에서 양식을 작성합니다.

    <h1>Add a new recipe!</h1>
            <form onSubmit={this.handleSumbit} >
    <fieldset>
                <div class="form-group">
                  <label for="inputDefault">Title</label>
                  <input 
                    type="inputDefault" 
                    name="title"
                    class="form-control" 
                    id="inputDefault"
                    placeholder="Enter title"
                    onChange={this.handleChange}
                    ></input>
                </div>
    <div className="form-group">
                    <label forHtml="textArea">Summary </label>
                    <textarea 
                      className="form-control"
                      id="textArea"
                      rows="3"
                      name="summary"
                      onChange={this.handleChange} 
                      placeholder="80 characters max"></textarea>
                </div>
    
    
    

    여기에는 많은 일들이 있지만 그 중 많은 것들이 문체에 관한 것입니다. onChange 이벤트는 제목 및 요약 변경 사항을 처리합니다.

    아래에 재료 및 단계 입력 필드를 추가했습니다.

     <div class="form-group">
                  <label>Ingredients</label>
                {this.renderIngredientInputs()}
                <button type="button" className="btn btn-primary" onClick={()=> this.addIngredientInputs()}>+ Add Ingredient</button>
                </div>
                <div class="form-group">
                  <label forHtml="textArea">Steps</label>
                  {this.renderStepInputs()}
                  <button type="button" className="btn btn-primary" onClick={()=> this.addStepInputs()}>+ Add Step</button>
                </div>
    

    마지막으로 submit 함수에 연결된 버튼을 작성합니다.

    <input type="submit" className="btn btn-secondary"></input>
              </fieldset>
            </form>
            </div>
            <div className="col-4"></div>
      </div>
    

    요약



    이 튜토리얼에서는 동적으로 제어되는 추가 레시피 양식을 작성했습니다. 우리는 단계와 함께 성분과 그 양을 추가할 수 있습니다. 필요한 경우 이 정보를 삭제할 수도 있습니다.

    좋은 웹페이지 즐겨찾기