【问题标题】:React.js - Show/Hide dynamically generated elements from componentReact.js - 显示/隐藏组件中动态生成的元素
【发布时间】:2017-12-29 12:02:48
【问题描述】:

我正在使用导入数据数组的组件模板生成表单字段元素。我希望能够隐藏其中一些元素,并在满足其他元素的条件时显示它们;这在表单字段中相当常见,例如当你选择A项时,表单域X出现,当你选择B项时,表单域X被隐藏。

我在 React 文档网站上的 conditional rendering 上阅读了相当多的内容,但这些示例并不能真正适用于我正在做的事情,尽管“防止组件渲染”部分已关闭。

made a Codepen 演示了我的设置,如果满足第一个字段的条件(在此示例中,第一个字段应输入 5 个字符),我想做的是显示第二个字段。我已经通过一个道具来设置初始隐藏,但是我怎样才能找到那个特定的隐藏元素并取消隐藏呢?

// Field data
const fieldData = [{
    "type": "text",
    "label": "First",
    "name": "first",
    "placeholder": "Enter first name",
    "hidden": false
  }, {
    "type": "text",
    "label": "Surname",
    "name": "surname",
    "placeholder": "Enter surname",
    "hidden": true
  }];

  // Get form data
  class FormData extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        items: props.data
      };
    }

    render() {
      let els = this.state.items;

      return els.map((el, i) => {
          return <Input key={i} params={el} />
      });
    }
  }

  // Input builder
  class Input extends React.Component {
      constructor(props) {
          super(props);

          // Keep state value
          this.state = {
              value: '',
              valid: false,
              hidden: this.props.params.hidden
          };

          this.handleChange = this.handleChange.bind(this);
      }

      handleChange(e) {
          this.setState({
              value: e.target.value,
              valid: e.target.value.length < 5 ? false : true
          });
      }

      render() {
          // Element attributes
          const {type, label, name, placeholder, hidden} = this.props.params;
          const isValid = this.state.valid === true ? <span>Valid! Should show Surname field.</span> : <span>Not valid. Should hide Surname field.</span>;

          if (!hidden) {
            return (
            <div>
                {label ? <label htmlFor={name}>{label}</label> : null}
                <input
                    type={type}
                    name={name}
                    placeholder={placeholder || null}
                    value={this.state.value}
                    onChange={this.handleChange}
                />
                {isValid}
            </div>
            );
          } else {
            return null;
          }
      }
  }

  // App
  class App extends React.Component {

    render() {
      return (
        <div>
            <h1>Show/Hide test</h1>
            <p>What we want here is the surname to appear when firstname has a value (say, it has 5 characters) and hide surname when firstname doesn't.</p>
            <FormData data={fieldData} />
        </div>
      );
    }
  }


  ReactDOM.render(
      <form>
        <App />
      </form>,
      document.getElementById('app')
  );

【问题讨论】:

  • 我会给字段一些唯一的 id 并将状态 1 组件提升到表单,在那里您将拥有一个按 id 提交状态的对象,即fieldStates: { '001': { value: '', valid: true, hidden: false }, '002': { ... } };。这样我就可以检查一个组件中不同字段的有效性并更改它们的可见性。
  • 这就是我在想的……我会使用FormData 中的key 属性来存储这些数据吗?在 React 中将数据发送回父组件是一种可接受的模式吗?
  • 我不确定我是否完全明白如何在这里使用 key 属性...我将fieldStates 存储在FormData 状态并在handleInputChange 处理程序中定义FormData 将传递给 Input 组件,并在此处理程序中更新常见的 fieldStates 状态。对不起,我不能给出更详细的例子,现在必须运行!如果我能提供帮助,当我回来时,我很乐意解释得更详细。
  • 不用担心,谢谢。我对 React 很陌生,所以仍在弄清楚其中的一些概念。

标签: javascript html reactjs


【解决方案1】:

您可以lift the state up 这样Input 的父级将处理状态和验证。
您可以有条件地调用 surname 属性或任何其他属性的“验证器”,前提是它存在,并与您自己约定验证方法将获得以下名称:propertNameValidator
因此,例如,当您对输入进行循环时,您可以检查是否存在 一个名为surnameValidator 的验证方法并针对hidden 属性调用它,您将传递Input,如果它不存在则只需传递false

以下是您的代码的一个小示例:

// Field data
const fieldData = [
  {
    type: "text",
    label: "First",
    name: "first",
    placeholder: "Enter first name",
    hidden: false
  },
  {
    type: "text",
    label: "Surname",
    name: "surname",
    placeholder: "Enter surname",
    hidden: true
  }
];

// Get form data
class FormData extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: props.data.map(el => ({...el, value: ''})) // add the value property
    };
  }

  onInputChange = (inputId, value) => {
    const { items } = this.state;
    const nextState = items.map((item) => {
      if (inputId !== item.name) return item;
      return {
        ...item,
        value,
      }
    });
    this.setState({ items: nextState });
  }

  surnameValidator = () => {
    const { items } = this.state;
    const nameElement = items.find(item => item.name === 'first');
    return nameElement && nameElement.value.length >= 5
  }

  render() {
    let els = this.state.items;

    return (
      <div>
        {
          els.map((el, i) => {
            const validator = this[`${el.name}Validator`];
            return (
              <Input
                key={i}
                {...el}
                inputId={el.name}
                hidden={validator ? !validator() : el.hidden}
                onInputChange={this.onInputChange}
              />
            );
          })
        }
      </div>
    )
  }
}

// Input builder
class Input extends React.Component {

  handleChange = ({ target }) => {
    const { inputId, onInputChange } = this.props;
    onInputChange(inputId, target.value);
  }

  render() {
    // Element attributes
    const { type, label, name, placeholder, hidden, value } = this.props;
    return (
      <div>
        {
          hidden ? '' : (
            <div>
              {label ? <label htmlFor={name}>{label}</label> : null}
              <input
                type={type}
                name={name}
                placeholder={placeholder || null}
                value={value}
                onChange={this.handleChange}
              />
            </div>
          )
        }
      </div>
    );
  }
}

// App
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Show/Hide test</h1>
        <p>
          What we want here is the surname to appear when firstname has a value
          (say, it has 5 characters) and hide surname when firstname doesn't.
        </p>
        <FormData data={fieldData} />
      </div>
    );
  }
}

ReactDOM.render(
  <form>
    <App />
  </form>,
  document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

如果您已经创建了元素并且只想隐藏/显示它,那么您可以有条件地添加或删除隐藏它的 CSS class

<input className={`${!isValid && 'hide'}`} />

【讨论】:

  • 感谢您的回答,但就像@raksheetbhat 我不想在渲染中创建新的输入元素,我想取消隐藏已经创建的元素。如果您查看 codepen 上的演示,姓氏尚未呈现,因为它设置为隐藏,但我希望它在创建名字时呈现。
  • 我的猜测是父组件FormData需要管理每个生成元素的显示/隐藏状态。
  • 好的,现在我明白了你的目标,一个组件不能也不应该控制其兄弟姐妹的状态。您可以列出状态并让父母控制孩子的状态。这是一直在做的。我会发布一个例子
  • 那太好了,谢谢!如果你能分叉我的例子就更好了。我对 React 很陌生,所以很难复制一些例子。 codepen.io/paulcredmond/pen/qpmWVX?editors=1010
  • @PaulRedmond 检查我编辑的答案,我希望这是一个很好的起点
【解决方案2】:

如果我没记错的话,您想要的是有条件地显示或隐藏输入元素。看看这是否有帮助。

render() {
      // Element attributes
      const {type, label, name, placeholder, hidden} = this.props.params;
      const isValid = this.state.valid === true ? <span>Valid! Should show Surname field.</span> : <span>Not valid. Should hide Surname field.</span>;

      if (!hidden) {
        return (
        <div>
            {label ? <label htmlFor={name}>{label}</label> : null}
            <input
                type={type}
                name={name}
                placeholder={placeholder || null}
                value={this.state.value}
                onChange={this.handleChange}
            />
            {this.state.valid && <input placeholder="add surname" />}
        </div>
        );
      } else {
        return null;
      }
 }

【讨论】:

  • 是和不是。我确实想显示一个新字段,但已经创建了一个,而不是在渲染中创建了一个。看看演示,谢谢!
  • @PaulRedmond 不清楚为什么要在渲染之外创建组件。您始终可以设置 CSS 类(或 hidden 属性),它们只会在视觉上隐藏元素
  • 我的意思是我不想像上面的答案那样创建一个新的输入。 Input 组件是一个模板,用于从数据数组中生成元素。我希望这些生成的元素之一来控制另一个的显示/隐藏。这就是挑战。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-29
  • 2017-03-02
  • 2011-06-22
  • 2018-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多