【问题标题】:*Help* Convert class to functional component using hooks*帮助*使用钩子将类转换为功能组件
【发布时间】:2019-12-01 12:34:03
【问题描述】:

我正在尝试将 EditableTabGroup 转换为功能组件 Tags 但是我似乎无法正确转换它,因为我正在尝试删除 this.

EditableTabGroup 工作正常,但是当我在 Taskform 中渲染 Tags 时它不起作用。

另外,我怎样才能清除状态标签,以便 onCreate(submit) 标签是一个空数组?

class EditableTagGroup extends React.Component {
  state = {
    tags: [],
    inputVisible: false,
    inputValue: ""
  };

  handleClose = removedTag => {
    const tags = this.state.tags.filter(tag => tag !== removedTag);
    console.log(tags);
    this.setState({ tags });
  };

  showInput = () => {
    this.setState({ inputVisible: true }, () => this.input.focus());
  };

  handleInputChange = e => {
    this.setState({ inputValue: e.target.value });
  };

  handleInputConfirm = () => {
    const { inputValue } = this.state;
    let { tags } = this.state;
    if (inputValue && tags.indexOf(inputValue) === -1) {
      tags = [...tags, inputValue];
    }
    console.log(tags);
    this.setState({
      tags,
      inputVisible: false,
      inputValue: ""
    });
  };

  saveInputRef = input => (this.input = input);

  forMap = tag => {
    const tagElem = (
      <Tag
        closable
        onClose={e => {
          e.preventDefault();
          this.handleClose(tag);
        }}
      >
        {tag}
      </Tag>
    );
    return (
      <span key={tag} style={{ display: "inline-block" }}>
        {tagElem}
      </span>
    );
  };

  render() {
    const { tags, inputVisible, inputValue } = this.state;
    const tagChild = tags.map(this.forMap);
    const { getFieldDecorator } = this.props;

    return (
      <div>
        <div style={{ marginBottom: 16 }}>
          <TweenOneGroup
            enter={{
              scale: 0.8,
              opacity: 0,
              type: "from",
              duration: 100,
              onComplete: e => {
                e.target.style = "";
              }
            }}
            leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
            appear={false}
          >
            {tagChild}
          </TweenOneGroup>
        </div>
        {inputVisible && (
          <Input
            ref={this.saveInputRef}
            onChange={this.handleInputChange}
            onPressEnter={this.handleInputConfirm}
            value={inputValue}
            onBlur={this.handleInputConfirm}
            type="text"
            size="small"
            style={{ width: 78 }}
          />
        )}
        {getFieldDecorator("tags", {
          initialValue: this.state.tags
        })(
          <Input
            ref={this.saveInputRef}
            type="text"
            size="small"
            style={{ display: "none" }}
          />
        )}
        {!inputVisible && (
          <Tag
            onClick={this.showInput}
            style={{ background: "#fff", borderStyle: "dashed" }}
          >
            <Icon type="plus" /> New Tag
          </Tag>
        )}
      </div>
    );
  }
}

export default EditableTagGroup;

【问题讨论】:

    标签: reactjs react-hooks react-props react-component react-ref


    【解决方案1】:

    所以,为了让它发挥作用,我改变了 2 件事:

    1) 您没有从其文件中导出标签。在此示例中,我将其导出为命名导出,但您可能应该将其导出为默认导出 (export default Tags)。

    2) 第二个问题出在这部分代码中:

     const handleInputConfirm = () => {
        if (inputValue && state.indexOf(inputValue) === -1) {
          let state = [...state, inputValue];
        }
        setState(state);
        setInputVisible(false);
        setInputValue("");
      };
    

    在 if 条件中,您检查当前标签和用户想要添加的标签,您定义一个 let“状态”。这里有2个问题。第一个是你在 if 块内分配了一个 let,这意味着它不能从块外部访问,因此 setState(state) 行只是将状态设置为相同的状态(状态是指状态变量state,而不是你在if块中定义的新state)。

    第二个问题并不是真正的问题,您只是不应该分配名称与上层作用域中的变量相同的新变量。正如您现在可能理解的那样,这是一种不好的做法。

    详细了解let 及其范围规则here

    这是Tags的完整工作代码:

    import React, { useState } from "react";
    import { Tag, Input, Icon } from "antd";
    import { TweenOneGroup } from "rc-tween-one";
    
    export const Tags = props => {
      const [state, setState] = useState([]);
      const [inputVisible, setInputVisible] = useState(false);
      const [inputValue, setInputValue] = useState("");
    
      const handleClose = removedTag => {
        const tags = state.filter(tag => tag !== removedTag);
        setState(tags);
      };
    
      const showInput = () => {
        setInputVisible(true);
      };
    
      const handleInputChange = e => {
        setInputValue(e.target.value);
      };
    
      const handleInputConfirm = () => {
        if (inputValue && state.indexOf(inputValue) === -1) {
          var newState = [...state, inputValue];
          setState(newState);
        }
        setInputVisible(false);
        setInputValue("");
      };
    
      const saveInputRef = input => (input = input);
    
      const forMap = tag => {
        const tagElem = (
          <Tag
            closable
            onClose={e => {
              e.preventDefault();
              handleClose(tag);
            }}
          >
            {tag}
          </Tag>
        );
        return (
          <span key={tag} style={{ display: "inline-block" }}>
            {tagElem}
          </span>
        );
      };
    
      const tagChild = state.map(forMap);
      const { getFieldDecorator } = props;
    
      return (
        <div>
          <div style={{ marginBottom: 16 }}>
            <TweenOneGroup
              enter={{
                scale: 0.8,
                opacity: 0,
                type: "from",
                duration: 100,
                onComplete: e => {
                  e.target.style = "";
                }
              }}
              leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
              appear={false}
            >
              {tagChild}
            </TweenOneGroup>
          </div>
          {inputVisible && (
            <Input
              ref={saveInputRef}
              onChange={handleInputChange}
              onPressEnter={handleInputConfirm}
              value={inputValue}
              onBlur={handleInputConfirm}
              type="text"
              size="small"
              style={{ width: 78 }}
            />
          )}
          {getFieldDecorator("tags", {
            initialValue: state.tags
          })(
            <Input
              ref={saveInputRef}
              type="text"
              size="small"
              style={{ display: "none" }}
            />
          )}
          {!inputVisible && (
            <Tag
              onClick={showInput}
              style={{ background: "#fff", borderStyle: "dashed" }}
            >
              <Icon type="plus" /> New Tag
            </Tag>
          )}
        </div>
      );
    };
    
    

    至于重置标签,你可以在Taskform.js内部定义state状态,并将其作为props传递给Tags。这样您就可以在Taskform.js 上重置state (setState([]))。

    Taskform.js:

    const [tags, setTags] = useState([]);
    
    const handleCreate = () => {
        form.validateFields((err, values) => {
          if (err) {
            return;
          }
    
          form.resetFields();
          onCreate(values);
          setTags([]);
        });
      };
    
    ...
    
    <Tags
       getFieldDecorator={getFieldDecorator}
       state={tags}
       setState={setTags}
    />
    

    标签.js:

    ...
    const { state, setState } = props;
    

    当然,您还应该从Tags.js 中删除[state, setState] = useState([])

    希望对你有帮助!

    【讨论】:

    • @SagiRika 感谢您的建设性反馈。一定漏掉了一些东西。我想知道。在表单上按“创建”后,如何处理要清除的标签?在您刷新页面之前,该状态是持久的。
    • 我编辑了答案以包含对您问题的答案。 @弗雷迪。
    • 很棒的@SagiRika 谢谢。您从标签中取出状态并将其添加到表单中。我将为表单中的大多数自定义组件执行此操作!你会说这种实现会导致长期的性能问题吗?
    • @SagiRika 似乎在 const tagChild = state.map(forMap);当你输入相同的标签时,
    • 我的错,setState(newState) 显然也应该在 if 语句中。现在检查代码。
    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2019-09-01
    • 2021-10-14
    • 2020-06-04
    • 2020-01-29
    相关资源
    最近更新 更多