【问题标题】:Convert class based to Function based compnent将基于类转换为基于函数的组件
【发布时间】:2021-01-06 11:16:40
【问题描述】:

我正在研究自动完成和自动填充反应。

我正在尝试将其转换为反应钩子,因为我编写的所有代码都只在钩子中。

根据我的理解,我在某种程度上将其转换为挂钩。但我无法完全转换它。

原码

import React, { Component } from "react";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      item: {
        code: "",
        name: "",
        unit: "",
        rate: ""
      },
      cursor: 0,
      searchItems: []
    };
    this.autocomplete = this.autocomplete.bind(this);
    this.handleKeyup = this.handleKeyup.bind(this);
    this.handleKeydown = this.handleKeydown.bind(this);
    this.handleListKeydown = this.handleListKeydown.bind(this);
    this.selectItem = this.selectItem.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  autocomplete(evt) {
    let text = evt.target.value;
    fetch(`https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`)
      .then((res) => res.json())
      .then((data) => {
        this.setState({ searchItems: data });
      });
  }

  handleKeyup(evt) {
    if (evt.keyCode === 27) {
      this.setState({ searchItems: [] });
      return false;
    }
  }

  handleKeydown(evt) {
    const { cursor, searchItems } = this.state;
    // arrow up/down button should select next/previous list element
    if (evt.keyCode === 38 && cursor > 0) {
      this.setState((prevState) => ({
        cursor: prevState.cursor - 1
      }));
    } else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
      this.setState((prevState) => ({
        cursor: prevState.cursor + 1
      }));
    }
    if (evt.keyCode === 13) {
      let currentItem = searchItems[cursor];
      if (currentItem !== undefined) {
        const { name, code, rate, unit } = currentItem;
        this.setState({ item: { name, code, rate, unit }, searchItems: [] });
      }
    }
    if (evt.keyCode === 8) {
      this.setState({ item: { name: "", code: "", rate: "", unit: "" } });
    }
  }

  selectItem(id) {
    const { searchItems } = this.state;
    let selectedItem = searchItems.find((item) => item.code === id);
    const { code, name, unit, rate } = selectedItem;
    this.setState({ item: { code, name, unit, rate } });
    this.setState({ searchItems: [] });
  }

  handleListKeydown(evt) {
    console.log(evt.keyCode);
  }

  handleChange(evt) {
    this.setState({ item: { [evt.target.name]: evt.target.value } });
  }

  render() {
    const { searchItems, cursor, item, handleChange } = this.state;
    const { code, name, unit, rate } = item;
    return (
      <div className="container mt-3">
        <h1 className="h2 text-center">Autocomplete Example</h1>
        <div className="form-group">
          <label htmlFor="autocomplete">Item Name </label>
          <input
            type="text"
            id="autocomplete"
            onChange={this.autocomplete}
            onKeyUp={this.handleKeyup}
            onKeyDown={this.handleKeydown}
            value={name}
            className="custom-input form-control"
          />
          {searchItems.length > 0 && (
            <ul className="list-group">
              {searchItems.map((item, idx) => (
                <li
                  className={
                    cursor === idx
                      ? "active list-group-item"
                      : "list-group-item"
                  }
                  key={idx}
                  onClick={() => this.selectItem(item.code)}
                  onKeyDown={(evt) => this.handleListKeydown(evt, item.code)}
                >
                  {item.name}
                </li>
              ))}
            </ul>
          )}
        </div>
      </div>
    );
  }
}

export default App;

原始代码链接:https://codepen.io/regexp/details/RwPNaLe

使用钩子

这是我尝试转换为钩子的代码。

import React, { useState } from "react";

export default function FunctionName(props) {
  const [item, setItem] = useState({
    vendorNameData: invoiceDetail[0].invoiceData.vendor,
    vendorAccountData: invoiceDetail[0].invoiceData.vendaAccount,
    vendorAddressData: invoiceDetail[0].invoiceData.vendorAddress
  });
  const [cursor, setCursor] = useState(0);
  const [searchItems, SetSearchItems] = useState([]);

  function AutoComplete(evt) {
    let text = evt.target.value;
    console.log(text);
    fetch(`https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`)
      .then((res) => res.json())
      .then((data) => {
        SetSearchItems(data);
      });
  }

  function HandleKeyUp(evt) {
    if (evt.keyCode === 27) {
      SetSearchItems([]);
      return false;
    }
  }

  function HandleKeyDown(evt) {
    // const [cursor, setCursor] = useState();
    // const [searchItems, SetSearchItems] = useState()

    if (evt.keyCode === 38 && cursor > 0) {
      setCursor((cursor) => ({ cursor: cursor + 1 }));
    } else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
      setCursor((cursor) => ({ cursor: cursor + 1 }));
    }
    if (evt.keyCode === 13) {
      let currentItem = searchItems[cursor];
      if (currentItem !== undefined) {
        const {
          vendorNameData,
          vendorAccountData,
          vendorAddressData
        } = currentItem;
        setItem({ vendorNameData, vendorAccountData, vendorAddressData });
        SetSearchItems([]);
      }
    }
    if (evt.keyCode === 8) {
      setItem({
        vendorNameData: "",
        vendorAccountData: "",
        vendorAddressData: ""
      });
    }
  }

  function SelectItem(id) {
    const [searchItems, SetSearchItems] = useState();
    let selectedItem = searchItems.find((item) => item.code === id);
    const {
      vendorNameData,
      vendorAccountData,
      vendorAddressData
    } = selectedItem;
    setItem({ vendorNameData, vendorAccountData, vendorAddressData });
    SetSearchItems([]);
  }

  function HandleListKeyDown(evt) {
    console.log(evt.keyCode);
  }

  function HandleChange(evt) {
    setItem({ item: { [evt.target.name]: evt.target.value } });
  }
}

指出我落后的地方真的很有帮助。我已经尽力了,但这是我能想到的。

任何帮助将不胜感激。

【问题讨论】:

    标签: javascript node.js reactjs react-hooks


    【解决方案1】:

    我已经“引述”并清理了您的原始组件,结果如下:(请参阅下面的注释)

    import React, {useState, useCallback} from 'react';
    
    function Input() {
        const [item, setItem] = useState({
            code: '',
            name: '',
            unit: '',
            rate: ''
        });
    
        const [cursor, setCursor] = useState(0);
    
        const [searchItems, setSearchItems] = useState([]);
    
        const autocomplete = useCallback((evt) => {
            const text = evt.target.value;
            fetch(
                `https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`
            )
                .then((res) => res.json())
                .then((data) => {
                    setSearchItems(data);
                });
        }, []);
    
        const handleKeyup = useCallback((evt) => {
            if (evt.keyCode === 27) {
                setSearchItems([]);
                return false;
            }
            return true;
        }, []);
    
        const handleKeydown = useCallback(
            (evt) => {
                // arrow up/down button should select next/previous list element
                if (evt.keyCode === 38 && cursor > 0) {
                    setCursor((prevCursor) => prevCursor - 1);
                } else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
                    setCursor((prevCursor) => prevCursor + 1);
                }
    
                if (evt.keyCode === 13) {
                    let currentItem = searchItems[cursor];
                    if (currentItem !== undefined) {
                        const {code, name, unit, rate} = currentItem;
                        setItem({code, name, unit, rate});
                        setSearchItems([]);
                    }
                }
    
                if (evt.keyCode === 8) {
                    setItem({code: '', name: '', unit: '', rate: ''});
                }
            },
            [cursor, searchItems]
        );
    
        const selectItem = useCallback(
            (id) => {
                let selectedItem = searchItems.find((item) => item.code === id);
                const {code, name, unit, rate} = selectedItem;
                setItem({code, name, unit, rate});
                setSearchItems([]);
            },
            [searchItems]
        );
    
        const handleListKeydown = useCallback((evt) => {
            console.log(evt.keyCode);
        }, []);
    
        return (
            <div className={'container mt-3'}>
                <h1 className={'h2 text-center'}>{'Autocomplete Example'}</h1>
                <div className={'form-group'}>
                    <label htmlFor={'autocomplete'}>{'Item Name'}</label>
                    <input
                        type={'text'}
                        id={'autocomplete'}
                        onChange={autocomplete}
                        onKeyUp={handleKeyup}
                        onKeyDown={handleKeydown}
                        value={item.name}
                        className={'custom-input form-control'}
                    />
                    {searchItems.length > 0 && (
                        <ul className={'list-group'}>
                            {searchItems.map((item, idx) => (
                                <li
                                    className={
                                        cursor === idx
                                            ? 'active list-group-item'
                                            : 'list-group-item'
                                    }
                                    key={idx}
                                    onClick={() => selectItem(item.code)}
                                    onKeyDown={handleListKeydown}>
                                    {item.name}
                                </li>
                            ))}
                        </ul>
                    )}
                </div>
            </div>
        );
    }
    
    export {Input};
    
    
    • useState 替换类组件状态管理。将您的状态尽可能分割成更小的部分是一种很好的做法,因为新值将完全替换旧值(没有像 Class Components this.setState 那样的合并)。以cursor 为例,执行const [cursor, setCursor] = useState(0); 来初始化你的状态(在这种情况下初始状态是0)。然后使用cursor 来使用该值并调用setCursor(newValue) 来更新它。每次更新状态时,您很可能会触发重新渲染。

    • useCallback 允许您仅在其依赖项发生更改时重新声明函数,从而提高性能。函数依赖项在第二个参数数组中指定。在每次渲染时,React 都会将新值与旧值进行比较,并且在依赖项没有改变时总是返回相同的函数。

    • return 语句替换了您之前的 render 方法。

    遵循有效的 sn-p。请注意,OP 原始代码行为没有改变。

    const {useState, useCallback, StrictMode} = React;
    
    function Input() {
        const [item, setItem] = useState({
            code: '',
            name: '',
            unit: '',
            rate: ''
        });
    
        const [cursor, setCursor] = useState(0);
    
        const [searchItems, setSearchItems] = useState([]);
    
        const autocomplete = useCallback((evt) => {
            const text = evt.target.value;
            fetch(
                `https://invoiceitems.herokuapp.com/items?name_like=${text}&_limit=6`
            )
                .then((res) => res.json())
                .then((data) => {
                    setSearchItems(data);
                });
        }, []);
    
        const handleKeyup = useCallback((evt) => {
            if (evt.keyCode === 27) {
                setSearchItems([]);
                return false;
            }
            return true;
        }, []);
    
        const handleKeydown = useCallback(
            (evt) => {
                // arrow up/down button should select next/previous list element
                if (evt.keyCode === 38 && cursor > 0) {
                    setCursor((prevCursor) => prevCursor - 1);
                } else if (evt.keyCode === 40 && cursor < searchItems.length - 1) {
                    setCursor((prevCursor) => prevCursor + 1);
                }
    
                if (evt.keyCode === 13) {
                    let currentItem = searchItems[cursor];
                    if (currentItem !== undefined) {
                        const {code, name, unit, rate} = currentItem;
                        setItem({code, name, unit, rate});
                        setSearchItems([]);
                    }
                }
    
                if (evt.keyCode === 8) {
                    setItem({code: '', name: '', unit: '', rate: ''});
                }
            },
            [cursor, searchItems]
        );
    
        const selectItem = useCallback(
            (id) => {
                let selectedItem = searchItems.find((item) => item.code === id);
                const {code, name, unit, rate} = selectedItem;
                setItem({code, name, unit, rate});
                setSearchItems([]);
            },
            [searchItems]
        );
    
        const handleListKeydown = useCallback((evt) => {
            console.log(evt.keyCode);
        }, []);
    
        return (
            <div className={'container mt-3'}>
                <h1 className={'h2 text-center'}>{'Autocomplete Example'}</h1>
                <div className={'form-group'}>
                    <label htmlFor={'autocomplete'}>{'Item Name'}</label>
                    <input
                        type={'text'}
                        id={'autocomplete'}
                        onChange={autocomplete}
                        onKeyUp={handleKeyup}
                        onKeyDown={handleKeydown}
                        value={item.name}
                        className={'custom-input form-control'}
                    />
                    {searchItems.length > 0 && (
                        <ul className={'list-group'}>
                            {searchItems.map((item, idx) => (
                                <li
                                    className={
                                        cursor === idx
                                            ? 'active list-group-item'
                                            : 'list-group-item'
                                    }
                                    key={idx}
                                    onClick={() => selectItem(item.code)}
                                    onKeyDown={handleListKeydown}>
                                    {item.name}
                                </li>
                            ))}
                        </ul>
                    )}
                </div>
            </div>
        );
    }
    
    ReactDOM.render(
        <StrictMode>
            <Input />
        </StrictMode>,
        document.getElementById('root')
    );
    .active {
      color: #ff0000;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
    
    <div id='root' />

    【讨论】:

    • 非常感谢您的回复...键码,一切正常...。但是 curson 无法正常工作,因为它没有在下拉列表中显示可点击的图标以供选择...。你能帮忙吗?
    • 另外你能告诉我这个 prevCursor 是从哪里来的吗......这意味着它的目的到底是什么?
    • @SangamBasnet prevCursor 只是前一个游标状态值(数字)的名称。当您将函数传递给状态更新器时,您会收到该状态的最新值作为参数。就像类组件中的 this.setState 一样。
    • @SangamBasnet 我刚刚用临时 CSS 更新了 sn-p 以使光标逻辑可见。
    猜你喜欢
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    相关资源
    最近更新 更多