npm 发布到github_如何编写一个简单的React搜索插件,将其发布到npm并将其部署到Github页面...

npm 发布到github

by Nirmalya Ghosh

由Nirmalya Ghosh

In this article, we’re going to write a simple search plugin in React. Through this article, I hope to help fellow developers understand how to write plugins using React, publish them to npm and deploy a demo to Github pages.

在本文中,我们将在React中编写一个简单的搜索插件。 通过本文,我希望可以帮助其他开发人员了解如何使用React编写插件,将其发布到npm以及将演示部署到Github页面。

The source code of the plugin is available on Github.

该插件的源代码可在Github上获得

入门 (Getting started)

We’re going to bootstrap our plugin using create-react-library which is a CLI for easily creating reusable react libraries. This CLI has a bunch of features and will help us in generating a boilerplate for our plugin.

我们将使用create-react-library引导我们的插件,这是一个CLI,可轻松创建可重用的react库。 此CLI具有很多功能 ,将帮助我们为插件生成样板。

To use create-react-library, we’ll need to install it globally:

要使用create-react-library,我们需要全局安装它:

npm install -g create-react-library

The above command will install create-react-library globally and we can generate a new module from any directory. To generate a new directory, type the following command in the directory where you want to bootstrap the plugin:

上面的命令将在全局安装create-react-library,我们可以从任何目录生成一个新模块。 要生成新目录,请在您要引导插件的目录中键入以下命令:

create-react-library

The above command will ask some basic prompts about your module and once you answer them, a boilerplate for the plugin will be generated.

上面的命令将询问有关模块的一些基本提示,一旦您回答了这些提示,便会生成该插件的样板。

Now, you need to run the plugin (for watching any changes that you make to it) and the example. In one tab, you can run:

现在,您需要运行该插件(用于观察您对其所做的任何更改)和示例。 在一个选项卡中,您可以运行:

cd react-search-box && yarn start

And, in another tab, you need to run the example app:

并且,在另一个选项卡中,您需要运行示例应用程序:

cd react-search-box/example && yarn start

The last command will run a create-react-app project which imports your plugin. If you make any changes to your plugin, it will get reflected in the example app. You can view the current status of your plugin by visiting http://localhost:3000.

最后一条命令将运行一个create-react-app项目,该项目将导入您的插件。 如果您对插件进行了任何更改,它将反映在示例应用程序中。 您可以通过访问http:// localhost:3000来查看插件的当前状态。

设计输入框 (Designing the input box)

Let’s add the first basic functionality: an input box which would let the users type in it.

让我们添加第一个基本功能:一个输入框,用户可以在其中输入内容。

import React, { Component } from 'react'import PropTypes from 'prop-types'
import styles from './styles.css'
export default class ReactSearchBox extends Component {  static propTypes = {    /**     * value: The default value for the input box.     * placeholder: The placeholder text for the input box.     */    value: PropTypes.string,    placeholder: PropTypes.string  }
state = {    value: ''  }
componentDidMount() {    const { value } = this.props
this.setState({      value: value    })  }
handleInputChange = e => {    const { value } = e.target
this.setState({      value: value    })  }
inputNode = () => {    /**     * This function is responsible for rendering the input box.     * The input box acts as a source of entry for the data from the user.     */    const { placeholder } = this.props    const { value } = this.state
return (      <input        className={styles.input}        type='text'        placeholder={placeholder}        value={value}        onChange={this.handleInputChange}      />    )  }
render() {    return <div className={styles.container}>{this.inputNode()}</div>  }}

In the above code, we’re creating an input element which has a className attribute, a type attribute, a placeholder attribute, a value attribute and an onChange handler. Most of these are very basic props. The only interesting prop is the onChange prop which is triggered whenever the user types in the input box.

在上面的代码中,我们正在创建一个具有className属性, type属性, placeholder属性, value属性和onChange处理程序的input元素。 这些大多数都是非常基本的道具。 唯一有趣的道具是onChange道具,只要用户在输入框中键入,就会触发它。

Whenever there is a change in the input box, we’re calling the handleInputChange function. handleInputChange function gets the event as an argument to it. We’re using an ES6 arrow function here. So, we don’t need to explicitly bind this with the handleInputChange function. You can read When should I use arrow functions with React by James K Nelson.

只要输入框发生更改,我们就会调用handleInputChange函数。 handleInputChange函数获取事件作为其参数。 我们在这里使用ES6箭头功能 。 因此,我们不需要将thishandleInputChange函数显式绑定。 您可以阅读James K Nelson 何时将箭头功能与React一起使用

Since we have a value state which we’re passing to the input box as an attribute, we are updating that value state whenever there is a change in the input box through the handleInputChange function.

由于我们有一个value状态,该value状态将作为属性传递给输入框,因此只要通过handleInputChange函数在输入框中发生更改,我们就会更新该value状态。

handleInputChange = e => {  const { value } = e.target
this.setState({    value  })}

If you visit http://localhost:3000, you’ll see an input box on the screen. You can type in the input box and the value will get updated.

如果您访问http:// localhost:3000 ,则会在屏幕上看到一个输入框。 您可以在输入框中键入内容,该值将得到更新。

If you check in React Developer Tools, you’ll see that the value of the input box is getting updated.

如果签入React Developer Tools ,您将看到输入框的值正在更新。

That’s all the functionality we need for the input box. Next, we’ll design a dropdown which will appear once the string that the user types in the input box, matches with any record that’ll be supplied to our plugin through the data prop.

这就是我们需要输入框的所有功能。 接下来,我们将设计一个下拉菜单,一旦用户在输入框中键入的字符串与将通过data prop提供给我们的插件的任何记录匹配,该下拉菜单就会出现。

设计下拉菜单 (Designing the dropdown)

In this section, we’re going to implement a dropdown, which will appear with an array of records which matches with the string that the user types in the input box. The initial array of records will be supplied using the data prop which we will implement first.

在本节中,我们将实现一个下拉列表,该下拉列表将显示与用户在输入框中键入的字符串匹配的记录数组。 记录的初始数组将使用我们将首先实现的data属性提供。

import React, { Component } from "react";import ReactSearchBox from "react-search-box";
export default class App extends Component {  data = [    {      key: "john",      value: "John Doe"    },    {      key: "jane",      value: "Jane Doe"    },    {      key: "mary",      value: "Mary Phillips"    },    {      key: "robert",      value: "Robert"    },    {      key: "karius",      value: "Karius"    }  ];
render() {    return (      <div className="container">        <ReactSearchBox          placeholder="Placeholder"          value="Doe"          data={this.data}        />      </div>    );  }}

Our plugin should be imported and defined like the above block of code. You import ReactSearchBox and then you pass an array of objects (the data array in this case) to ReactSearchBox.

我们的插件应该像上面的代码块一样被导入和定义。 导入ReactSearchBox ,然后将对象数组(在这种情况下为data数组)传递给ReactSearchBox

For now, we will render the dropdown if a value prop is passed to our plugin. Later, we will refactor our component to show the dropdown if any record from the data prop matches with the supplied value prop.

现在,如果将value道具传递给我们的插件,我们将渲染下拉列表。 稍后,如果data属性中的任何记录与提供的value属性匹配,我们将重构组件以显示下拉列表。

Our plugin would now look something like the following:

现在,我们的插件如下所示:

import React, { Component } from 'react'import PropTypes from 'prop-types'
import styles from './styles.css'
export default class ReactSearchBox extends Component {  static propTypes = {    /**     * value: The default value for the input box.     * placeholder: The placeholder text for the input box.     * data: An array of objects which acts as the source of data for the dropdown.     */    value: PropTypes.string,    placeholder: PropTypes.string,    data: PropTypes.array.isRequired  }
static defaultProps = {    /**     * Set data prop as an empty array in case it's not passed.     */    data: []  }
state = {    value: ''  }
componentDidMount() {    /**     * This function is the same as before     */    }
handleInputChange = e => {    /**     * This function is the same as before     */    }
inputNode = () => {    /**     * This function is the same as before     */  }
dropdownNode = () => {    /**     * This function is responsible for rendering the dropdown.     */    const { data } = this.props
return (      <div className={`react-search-box-dropdown ${styles.dropdown}`}>        <ul className={styles.dropdownList}>          {data.map(record => {            return (              <li                key={record.key}                className={`react-search-box-dropdown-list-item ${                  styles.dropdownListItem                }`}              >                {record.value}              </li>            )          })}        </ul>      </div>    )  }
render() {    return (      <div className={styles.container}>        {this.inputNode()}        {this.dropdownNode()}      </div>    )  }}

The code for the dropdown is present in the dropdownNode function. Based on the data prop which is supplied to our plugin, we are creating a list of li items and rendering inside the dropdown.

dropdownNode函数中提供了该dropdownNode代码。 基于提供给我们插件的data道具,我们将创建li项目列表并在下拉列表中进行渲染。

If we visit http://localhost:3000/, we will see a dropdown along with an input box.

如果访问http:// localhost:3000 / ,则会看到一个下拉列表以及一个输入框。

That’s all the functionality we need for the dropdown. Next, we’ll refactor our plugin to render dropdown only when any record matches with the query that the user will type in the input box.

这就是下拉菜单所需的全部功能。 接下来,我们将重构插件,使其仅在任何记录与用户将在输入框中键入的查询匹配时才呈现下拉列表。

重构我们的插件以仅当任何记录与查询匹配时才呈现下拉列表 (Refactoring our plugin to render dropdown only when any record matches with the query)

This is the last step of our development process.

这是我们开发过程的最后一步。

First, we need to add a package named fuse.js which is a lightweight fuzzy-search library. It’ll help us to check if the query that the user types in the input box matches with any records from the data prop which is supplied to our plugin.

首先,我们需要添加一个名为fuse.js的软件包,这是一个轻量级的模糊搜索库。 这将帮助我们检查用户在输入框中键入的查询是否与提供给我们插件的data prop中的任何记录匹配。

Let’s add it to our list of dependencies using the command below:

让我们使用以下命令将其添加到我们的依赖项列表中:

yarn add fuse.js

Now, we would refactor our plugin to check if the query matches with any of the records.

现在,我们将重构插件以检查查询是否与任何记录匹配。

import React, { Component } from 'react'import PropTypes from 'prop-types'import Fuse from 'fuse.js'
import styles from './styles.css'
export default class ReactSearchBox extends Component {  static propTypes = {    /**     * This is same as before     */  }
static defaultProps = {    /**     * This is same as before     */  }
state = {    /**     * 'matchedRecords' stores the items when the input box's value     * matches with any item from the 'data' prop.     */    value: '',    matchedRecords: []  }
constructor(props) {    super(props)
const { data } = props
/**     * These options are from Fuse plugin. Check out http://fusejs.io/     * for more details.     */    const options = {      /**       * At what point does the match algorithm give up. A threshold of 0.0       * requires a perfect match (of both letters and location), a threshold       * of 1.0 would match anything.       */      threshold: 0.05,      /**       * Determines approximately where in the text is the pattern expected to be found.       */      location: 0,      /**       * Determines how close the match must be to the fuzzy location       * (specified by location). An exact letter match which is distance       * characters away from the fuzzy location would score as a complete       * mismatch. A distance of 0 requires the match be at the exact       * location specified, a distance of 1000 would require a perfect       * match to be within 800 characters of the location to be found       * using a threshold of 0.8.       */      distance: 100,      /**       * When set to include matches, only the matches whose length exceeds this       * value will be returned. (For instance, if you want to ignore single       * character index returns, set to 2).       */      minMatchCharLength: 1,      /**       * List of properties that will be searched. This supports nested properties,       * weighted search, searching in arrays of strings and objects.       */      keys: ['value']    }
this.fuse = new Fuse(data, options)  }
componentDidMount() {    const { value } = this.props
/**     * If any 'value' is passed as prop, find if it matches with any item     * from teh 'data' prop. If there is any record, which matches with     * the query, update 'matchedRecord' state with the matched object(s).     *     * Also, update the 'value' state with the 'value' prop.     */    const matchedRecords = this.fuse.search(value)
this.setState({      value: value.trim(),      matchedRecords,      /**       * Control the showing and hiding of the dropdown when there is any value       * in the input box. But, close the dropdown once any dropdown item is       * clicked.       */      showDropdown: !!value.trim()    })  }
handleInputChange = e => {    /**     * This function is responsible for checking if any items from the input     * box's value matches with any item form the 'data' prop. If any item matches,     * then that matched object is pushed into the 'matchedRecords' state. That     * state is responsible for populating the dropdown.     */
const { value } = e.target
/**     * Check all the values from 'data' array whose 'value' matches with     * 'value' using Fuse plugin.     */    const matchedRecords = this.fuse.search(value)
/**     * Update 'value' state with the value from the input box     * Update 'matchedRecords' state with the matched records from the data array.     */    this.setState({      value: value.trim(),      matchedRecords,      /**       * Show the dropdown onChange of the input       */      showDropdown: true    })  }
inputNode = () => {    /**     * This function is the same as before     */  }
handleDropdownItemClick = record => {    /**     * This function is responsible for updating the value inside the     * input box when any dropdown item is clicked.     *     * The 'value' state is updated with the clicked record's value.     */
const { value } = record
this.setState({      value,      /**       * Hide the dropdown once any dropdown item is clicked       */      showDropdown: false    })  }
dropdownNode = () => {    /**     * This function is responsible for rendering the dropdown.     * When any value from the input box matches with any value from the     * 'data' prop, that matched object from the 'data' array shows up     * in the dropdown's li. The matched values are stored in the     * 'matchedRecords' state.     */    const { matchedRecords, showDropdown } = this.state
/**     * If there is no value present in the input box, then the dropdown     * shouldn't appear.     */    if (!showDropdown) return false
return (      <div className={`react-search-box-dropdown ${styles.dropdown}`}>        <ul className={styles.dropdownList}>          {matchedRecords.map(record => {            return (              <li                key={record.key}                className={`react-search-box-dropdown-list-item ${                  styles.dropdownListItem                }`}                onClick={() => this.handleDropdownItemClick(record)}              >                {record.value}              </li>            )          })}        </ul>      </div>    )  }
render() {    /**     * This function is the same as before     */  }}

I’ve added comments inside every function which state what that particular function does. The basic functionality that we get out of the above code is the following:

我已经在每个函数中添加了注释,指出了该特定函数的功能。 我们从以上代码中获得的基本功能如下:

  1. The user types in the input box (We would call the text that user types in as query).

    用户在输入框中键入内容(我们会将用户键入的文本称为query )。

  2. onChange of the input box, the plugin would check if the current value of the input box matches with any record supplied to our plugin through the data prop.

    在输入框的onChange上,插件将检查输入框的当前值是否与通过data prop提供给我们的插件的任何记录相匹配。

  3. If any record matches with the query, we will render a dropdown with a list of the matched records.

    如果有任何记录与查询匹配,我们将呈现一个包含匹配记录列表的下拉列表。
  4. If no records match with the query, we won’t render the dropdown.

    如果没有记录与查询匹配,我们将不会显示下拉列表。

If you visit http://localhost:3000, you can see that dropdown appears with a list of matched records. The dropdown will hide if the input box is empty.

如果您访问http:// localhost:3000 ,则可以看到该下拉列表显示了匹配记录的列表。 如果输入框为空,则下拉菜单将隐藏。

That’s all the code that we need. Next, we will push our changes to a Github repository.

这就是我们需要的所有代码。 接下来,我们会将更改推送到Github存储库。

将我们的代码推送到Github (Pushing our code to Github)

In this section, we would create a Github repository and push our code to Github.

在本节中,我们将创建一个Github存储库,并将我们的代码推送到Github。

If you’re new to Github, you can follow this article to know how to create a repository. Once you’re done creating a new repository, you need to add remote to your plugin.

如果您是Github的新手,可以阅读本文以了解如何创建存储库。 创建完新存储库后,需要将remote添加到您的plugin中

git remote add origin https://github.com/ghoshnirmalya/react-search-box

In my case, I’m adding https://github.com/ghoshnirmalya/react-search-box because I want my code changes to be available on that repository. For your case, it’ll be a different url.

就我而言,我要添加https://github.com/ghoshnirmalya/react-search-box因为我希望代码更改在该存储库中可用。 对于您的情况,它将是一个不同的URL。

Once that’s done, you can push your changes to the Github repository:

完成后,您可以将更改推送到Github存储库:

git push origin master

You code is now available on your Github repository.

您的代码现在可以在Github存储库中使用。

将我们的插件发布到npm (Publishing our plugin to npm)

In this section, we will publish our code to npm. npm is the package manager for JavaScript.

在本节中,我们将代码发布到npm 。 npm是JavaScript的软件包管理器。

create-react-library already has a feature through which we can publish our plugin to the npm registry. You just need to run the following command:

create-react-library已经具有一项功能,通过它我们可以将插件发布到npm注册表。 您只需要运行以下命令:

yarn publish

将示例应用程序部署到Github页面 (Deploying an example application to Github pages)

In this section, we will deploy a sample application which will use our plugin to Github pages.

在本节中,我们将部署一个示例应用程序,该应用程序将使用我们的插件到Github pages

create-react-library already has a feature through which we can deploy the example folder to Github pages. You just need to run the following command:

create-react-library已经具有一项功能,通过它我们可以将示例文件夹部署到Github页面。 您只需要运行以下命令:

yarn deploy

Now, you can view your application available at https://your-username.github.io/your-repository-name/. For me, it’s https://ghoshnirmalya.github.io/react-search-box since the url to my repository is https://github.com/ghoshnirmalya/react-search-box.

现在,您可以在https://your-username.github.io/your-repository-name/查看您的应用程序。 对我来说,它是https://ghoshnirmalya.github.io/react-search-box,因为我存储库的网址是https://github.com/ghoshnirmalya/react-search-box

结束语 (Closing notes)

One last thing that you should remember is that I’ve made a bunch of changes to React Search Box on top of the changes that I mentioned here. I just wanted to create a simple autocomplete React plugin and thought that my learnings would help others who want to contribute to React but are not sure how to start.

您应该记住的最后一件事是,我在这里提到的更改之上对React Search Box进行了很多更改。 我只是想创建一个简单的自动完成React插件,并认为我的学习将对希望为React做出贡献但不确定如何开始的其他人有所帮助。

I hope that this article will help others. I’m curious to know what great plugins you guys build with the help of this articles. Please let me know in the comments below.

我希望本文能对其他人有所帮助。 我很想知道你们在本文的帮助下构建了哪些出色的插件。 请在下面的评论中让我知道。

翻译自: https://www.freecodecamp.org/news/how-to-write-a-simple-react-search-plugin-publish-it-to-npm-and-deploy-it-to-github-pages-d8876dff7780/

npm 发布到github

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值