一个一个地定位 map() 的项目

Target map()'ed items one by one

我想创建一个联系表单,表单字段在填写后会发生变化。 例如,当您填写电子邮件字段时,您按下回车键或单击“下一步”按钮,它会消失并出现下一个字段。将有不同的表单字段类型(文本、电子邮件、select 等)。 我想要实现的是基于 this 形式,但它是用 vanilla js 编码的,我想从头开始用 React 编写它。

现在我的基本代码是这样的:

ContactForm.js 文件:

import { useState, useEffect } from "react";
import Fields from "./Fields";

const ContactForm = ({ fields }) => {
  const [current, setCurrent] = useState(0);
  const [show, setShow] = useState(true);
  
  return (
    <div className='container'>
      <div className='fs-form-wrap' id='fs-form-wrap'>
        <Fields fields={fields} isCurrent={current} />
        <button onClick={() => setCurrent(current + 1)}>Continue</button>
      </div>
    </div>
  );
};

Fields.js 文件:

import { useState } from "react";
import Field from "./Field";

const Fields = ({ fields, isCurrent }) => {

  return (
    <form id='myform' className='fs-form fs-form-full'>
      <ol className='fs-fields'>
        {fields.map((field, index) => {
          return (
            <Field
              placeHolder={field.placeholder}
              title={field.title}
              type={field.type}
              key={index}
              isCurrent={isCurrent}
            />
          );
        })}
      </ol>
    </form>
  );
};

export default Fields;

Field.js 文件:

import { useState, useEffect } from "react";
import styled from "styled-components";

const Field = ({ placeHolder, type, title, isCurrent }) => {

  return (
    <TheField isCurrent={isCurrent}>
      <label className='fs-field-label fs-anim-upper' htmlFor='q2'>
        {title}
      </label>
      <input
        className='fs-anim-lower'
        id='q2'
        name='q2'
        type={type}
        placeholder={placeHolder}
        required
      />
    </TheField>
  );
};

export default Field;

const TheField = styled.li`
  visibility: ${(props) => (props.isCurrent === 0 ? "visible" : "hidden")};
`;

基于这段代码,最初我得到了来自我的 dummy-data.json 文件的 2 个字段,但是当我单击按钮时,它们都消失了。 我知道我的代码仍然是一团糟,但我首先想让它们一个一个出现,然后我想我知道其他部分的逻辑了。

如有任何帮助,我们将不胜感激。

使用下面@zergski 的解决方案进行编辑:

import { useState, useEffect } from "react";
import Field from "./Field";
import { BidirectionalIterator } from "./Iterator";

const ContactForm = ({ fields }) => {
  const [current, setCurrent] = useState(0);
  const [show, setShow] = useState(true);


  const options = { startIndex: 0, loop: false, clamp: true };

  const list = new BidirectionalIterator(fields, options);
  
  return (
    <div className='container'>
      <div className='fs-form-wrap' id='fs-form-wrap'>
        <form id='myform' className='fs-form fs-form-full'>
          <ol className='fs-fields'>
            <li>{list}</li>
            {/* {fields.map((field, index) => {
          return (
            <Field
              placeHolder={field.placeholder}
              title={field.title}
              type={field.type}
              key={index}
            />
          );
        })} */}
          </ol>
          {/* <Submit clickHandler={submitClickHandler} text={submitText} /> */}
        </form>
        <button onClick={() => list.next()}>Continue</button>
      </div>
    </div>
  );
};

您需要创建自己的迭代器...通过使用生成器或自定义 class..这是我写的一个

export class BidirectionalIterator {
   // TODO: support for other data types
   // #iterate_over: string = 'index'

   static index: number = 0
   static start_index: number = 0
   static looped: boolean = false
   static clamped: boolean = true

   static data: PropertyKey[]
   static ct: number = 0
   static data_len: number = 0

   /** Only supports iterables for now.
    * @param data - the object to be iterated
    * @param options.startIndex - A negative value of less than 0 sets the index at the end of the iterable
    * @param options.loop - loop to the opposite end of iterable (overrides the clamp option setting)
    * @param options.clamp - iterator won't finish upon reaching iterable bounds
    *
    * @caution - DO NOT use a for of/in loop on the iterator if either the loop option is set to true!
   */
   constructor(data: any[] = [], { startIndex = 0, loop = false, clamp = true }: BidirectionalIteratorOptions = {}) {
      BidirectionalIterator.setData(data)
      BidirectionalIterator.start_index = startIndex
      BidirectionalIterator.clamped = loop ? false : clamp
      BidirectionalIterator.looped = loop
   }


   static [Symbol.iterator]() {
      return this
   }

   static setData(data: any) {
      this.data = data

      // TODO: finish support for different collection types
      let [ct, data_len] =
         data instanceof Array ? [1, data.length]
            : data instanceof Map ? [2, data.size]
               : data instanceof Set ? [3, data.size]
                  : [4, -1]

      this.ct = ct
      this.data_len = data_len

      this.resetIndex()
   }

   static resetIndex() {
      this.setIndex(this.start_index < -1 ? this.len : this.start_index)
   }

   static setIndex(idx: number) {
      this.index = idx - 1
   }


   static get len(): number {
      return this.data.length
   }


   // ! well I'm definitely repeating myself....
   static get entry() {
      this.index = num_between(this.index, 0, this.len - 1)
      return {
         index: this.index,
         value: this.data[this.index]
      }
   }

   static get next_entry() {
      this.index = num_between(this.index, 0, this.len - 1)
      return {
         index: this.index + 1,
         value: this.data[this.index] || (this.looped ? this.data[0] : null)
      }
   }

   static get prev_entry() {
      this.index = num_between(this.index, 0, this.len - 1)
      return {
         index: this.index - 1,
         value: this.data[this.index + 1] || (this.looped ? this.data[this.len - 1] : null)
      }
   }


   static next() {
      let value, done

      (done = this.index >= this.len)
         ? this.index = this.len
         : done = ++this.index >= this.len

      // value = this.data[done ? this.len-1 : this.index]
      value = this.data[num_between(this.index, 0, this.len)]


      if (done)
         this.looped
            ? value = this.data[this.index = 0]
            : this.clamped
               ? value = this.data[this.len - 1] : null


      return {
         index: this.index,
         value,
         done
      }
   }


   static prev() {
      let value, done

      (done = this.index <= -1)
         ? this.index = -1
         : done = --this.index <= -1

      // value = this.data[done ? 0 : this.index]
      value = this.data[num_between(this.index, 0, this.len)]


      if (done)
         this.looped
            ? value = this.data[this.len - 1]
            : this.clamped
               ? value = this.data[0] : null


      return {
         index: this.index,
         value,
         done
      }
   }
}

所以要使用它只需实例化 class..

const list = new BidirectionalIterator(data_array, options)

// and use with .next() & .prev() methods on mouse input
// e.g this will return the next entry in given array
list.next()

虽然它是用打字稿写的,所以你需要删除所有类型声明