一个一个地定位 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()
虽然它是用打字稿写的,所以你需要删除所有类型声明
我想创建一个联系表单,表单字段在填写后会发生变化。 例如,当您填写电子邮件字段时,您按下回车键或单击“下一步”按钮,它会消失并出现下一个字段。将有不同的表单字段类型(文本、电子邮件、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()
虽然它是用打字稿写的,所以你需要删除所有类型声明