Nigel  Uys

Nigel Uys

1668761897

What are Pure Components in React?

Let's learn React pure component and how to implement one.

By default, React will always re-render the component each time there is a change in state or props value. A Pure Component is a component that performs a check on the value of React props before deciding whether to re-render the component or not. Let me show you by an example.

Class-based PureComponent

Imagine you have an application that displays a movie title and release status:

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      title: "Titanic",
      isReleased: true
    };
  }

  toogleRelease = () => {
    console.log("toogleRelease");
    this.setState((prevState) => ({
      isReleased: !prevState.isReleased
    }));
  };

  render() {
    const { title, isReleased } = this.state;
    return (
      <>
        <MovieTitle title={title} />
        <h1>Movie is released: {isReleased ? "yes" : "no"} </h1>
        <button onClick={this.toogleRelease}>Toogle Release</button>
      </>
    );
  }
}

Next, you have a MovieTitle component with the following content:

class MovieTitle extends React.Component {
  render() {
    console.log("MovieTitle is rendered");
    return <h1>Movie title {this.props.title} </h1>;
  }
}

Here’s a running demo.

When you change the isReleased value by click on the button, you’ll find that MovieTitle gets re-rendered because React will print the log to the console. But actually, MovieTitle component doesn’t need to re-render because even if the value of isReleased is changed, its output remains the same.

To prevent the re-rendering and optimize your React app performance, you can extends the PureComponent class instead of Component:

class MovieTitle extends React.PureComponent {

}

Now when you click on toogle release, you won’t see React printing the log because MovieTitle will check the props and determine whether to render or not.

function-based PureComponent

You can do the same with function-based components, but instead of extending the PureComponent class, you need to wrap the function component with React.memo(), which memoize (cache) the output of the component.

The function will return a memoized component that you can call from other components:

function MovieTitle(){

}

const MemoizedMovie = React.memo(MovieTitle)

Now you just need to call the memoized MovieTitle inside App component:

render() {
  return (
    <MemoizedMovie title={title} />
  );
}

React’s “pure component” is a performance optimization method which implements the shouldComponentUpdate method underneath, but you should only use it sparingly, or even none at all.

This is because the performance optimization gained from using pure components are very trivial. React already re-render components very fast (as in less than 100 milliseconds fast, mostly around 20ms in small applications)

When you use pure components, comparing states and props will consume memory storage.

There will be a little delay because React has to compare the states and props before deciding whether to render or not. When React doesn’t re-render the component, you will gain a few milliseconds. When React does re-render, you will be slowed by a few milliseconds. Most people won’t be able to tell the difference anyway.

Original article source at: https://sebhastian.com/

#react #component 

What are Pure Components in React?
Gordon  Matlala

Gordon Matlala

1668745159

Learn ShouldComponentUpdate and When You Should Use It

Understanding React shouldComponentUpdate

Learn shouldComponentUpdate and when you should use it


During React component’s lifecycle, there’s a set of specific methods that gets executed during a certain moment. These methods are called lifecycle methods, and shouldComponentUpdate is a part of this method group.

This method is used to let your component exit the update lifecycle and prevent re-rendering of the component if there’s no meaningful change. The method will receive two arguments: the next props and state that are being received. You can then compare these data to the current data.

Here’s an implementation example:

shouldComponentUpdate(nextProps, nextState) {
  if (this.props.color !== nextProps.color) {
    return true;
  }
  if (this.state.count !== nextState.count) {
    return true;
  }
  return false;
}

The method is run right after React internal state is updated but before the component re-renders and the update is reflected in the browser:

Source: https://git.io/JkHrp

Source: https://git.io/JkHrp

By default, this method is not implemented into React, and the most optimum implementation of this method can be found in React’s PureComponent API.

Should you use shouldComponentUpdate

Through my experience in creating React applications, I rarely encounter a case where the use of componentShouldUpdate is justified. The reason is that React rendering execution is already fast by default. If you check on React’s performance. Most component render would take about 1 to 2 milliseconds. On a very complicated component with lots of computation, it might take React 10 milliseconds.

Let’s say you implemented shouldComponentUpdate manually and you reduced React’s render time from 10 milliseconds to 1 milliseconds. You just created a 10x improvement, sure. But I bet you won’t feel any different at all. That’s because we’re humans, and we won’t be able to discern a significant improvement in load speed between 10ms and 1 ms.

In general, this method shouldn’t be used at all. Or if you really need it, you should implement Pure Component API so React can maintain it for you.

Original article source at: https://sebhastian.com/react-shouldcomponentupdate/

#react #component #update 

Learn ShouldComponentUpdate and When You Should Use It

Error-handler: The ErrorHandler Component Provides tools

ErrorHandler Component

The ErrorHandler component provides tools to manage errors and ease debugging PHP code.

Getting Started

$ composer require symfony/error-handler
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\ErrorHandler\DebugClassLoader;

Debug::enable();

// or enable only one feature
//ErrorHandler::register();
//DebugClassLoader::enable();

// If you want a custom generic template when debug is not enabled
// HtmlErrorRenderer::setTemplate('/path/to/custom/error.html.php');

$data = ErrorHandler::call(static function () use ($filename, $datetimeFormat) {
    // if any code executed inside this anonymous function fails, a PHP exception
    // will be thrown, even if the code uses the '@' PHP silence operator
    $data = json_decode(file_get_contents($filename), true);
    $data['read_at'] = date($datetimeFormat);
    file_put_contents($filename, json_encode($data));

    return $data;
});

Resources

Download Details:

Author: Symfony
Source Code: https://github.com/symfony/error-handler 
License: MIT license

#php #symfony #errors #component 

Error-handler: The ErrorHandler Component Provides tools

Polyfill: PHP Polyfills

Symfony Polyfill

This project backports features found in the latest PHP versions and provides compatibility layers for some extensions and functions. It is intended to be used when portability across PHP versions and extensions is desired.

Polyfills are provided for:

  • the apcu extension when the legacy apc extension is installed;
  • the ctype extension when PHP is compiled without ctype;
  • the mbstring and iconv extensions;
  • the uuid extension;
  • the MessageFormatter class and the msgfmt_format_message functions;
  • the Normalizer class and the grapheme_* functions;
  • the utf8_encode and utf8_decode functions from the xml extension or PHP-7.2 core;
  • the Collator, NumberFormatter, Locale and IntlDateFormatter classes, limited to the "en" locale;
  • the intl_error_name, intl_get_error_code, intl_get_error_message and intl_is_failure functions;
  • the idn_to_ascii and idn_to_utf8 functions;
  • the hex2bin function, the CallbackFilterIterator, RecursiveCallbackFilterIterator and SessionHandlerInterface classes introduced in PHP 5.4;
  • the array_column, boolval, json_last_error_msg and hash_pbkdf2 functions introduced in PHP 5.5;
  • the password_hash and password_* related functions introduced in PHP 5.5, provided by the ircmaxell/password-compat package;
  • the hash_equals and ldap_escape functions introduced in PHP 5.6;
  • the *Error classes, the error_clear_last, preg_replace_callback_array and intdiv functions introduced in PHP 7.0;
  • the random_bytes and random_int functions introduced in PHP 7.0, provided by the paragonie/random_compat package;
  • the PHP_INT_MIN constant introduced in PHP 7.0,
  • the SessionUpdateTimestampHandlerInterface interface introduced in PHP 7.0,
  • the is_iterable function introduced in PHP 7.1;
  • a Binary utility class to be used when compatibility with mbstring.func_overload is required;
  • the spl_object_id and stream_isatty functions introduced in PHP 7.2;
  • the mb_ord, mb_chr and mb_scrub functions introduced in PHP 7.2 from the mbstring extension
  • the sapi_windows_vt100_support function (Windows only) introduced in PHP 7.2;
  • the PHP_FLOAT_* constant introduced in PHP 7.2;
  • the PHP_OS_FAMILY constant introduced in PHP 7.2;
  • the is_countable function introduced in PHP 7.3;
  • the array_key_first and array_key_last functions introduced in PHP 7.3;
  • the hrtime function introduced in PHP 7.3;
  • the JsonException class introduced in PHP 7.3;
  • the get_mangled_object_vars, mb_str_split and password_algos functions introduced in PHP 7.4;
  • the fdiv function introduced in PHP 8.0;
  • the get_debug_type function introduced in PHP 8.0;
  • the preg_last_error_msg function introduced in PHP 8.0;
  • the str_contains function introduced in PHP 8.0;
  • the str_starts_with and str_ends_with functions introduced in PHP 8.0;
  • the ValueError class introduced in PHP 8.0;
  • the UnhandledMatchError class introduced in PHP 8.0;
  • the FILTER_VALIDATE_BOOL constant introduced in PHP 8.0;
  • the get_resource_id function introduced in PHP 8.0;
  • the Attribute class introduced in PHP 8.0;
  • the Stringable interface introduced in PHP 8.0;
  • the PhpToken class introduced in PHP 8.0 when the tokenizer extension is enabled;
  • the array_is_list function introduced in PHP 8.1;
  • the enum_exists function introduced in PHP 8.1;
  • the MYSQLI_REFRESH_REPLICA constant introduced in PHP 8.1;
  • the ReturnTypeWillChange attribute introduced in PHP 8.1;
  • the AllowDynamicProperties attribute introduced in PHP 8.2;
  • the SensitiveParameter attribute introduced in PHP 8.2;
  • the SensitiveParameterValue class introduced in PHP 8.2;

It is strongly recommended to upgrade your PHP version and/or install the missing extensions whenever possible. This polyfill should be used only when there is no better choice or when portability is a requirement.

Compatibility notes

To write portable code between PHP5 and PHP7, some care must be taken:

  • \*Error exceptions must be caught before \Exception;
  • after calling error_clear_last(), the result of $e = error_get_last() must be verified using isset($e['message'][0]) instead of null !== $e.

Usage

When using Composer to manage your dependencies, you should not require the symfony/polyfill package, but the standalone ones:

  • symfony/polyfill-apcu for using the apcu_* functions,
  • symfony/polyfill-ctype for using the ctype functions,
  • symfony/polyfill-php54 for using the PHP 5.4 functions,
  • symfony/polyfill-php55 for using the PHP 5.5 functions,
  • symfony/polyfill-php56 for using the PHP 5.6 functions,
  • symfony/polyfill-php70 for using the PHP 7.0 functions,
  • symfony/polyfill-php71 for using the PHP 7.1 functions,
  • symfony/polyfill-php72 for using the PHP 7.2 functions,
  • symfony/polyfill-php73 for using the PHP 7.3 functions,
  • symfony/polyfill-php74 for using the PHP 7.4 functions,
  • symfony/polyfill-php80 for using the PHP 8.0 functions,
  • symfony/polyfill-php81 for using the PHP 8.1 functions,
  • symfony/polyfill-php82 for using the PHP 8.2 functions,
  • symfony/polyfill-iconv for using the iconv functions,
  • symfony/polyfill-intl-grapheme for using the grapheme_* functions,
  • symfony/polyfill-intl-idn for using the idn_to_ascii and idn_to_utf8 functions,
  • symfony/polyfill-intl-icu for using the intl functions and classes,
  • symfony/polyfill-intl-messageformatter for using the intl messageformatter,
  • symfony/polyfill-intl-normalizer for using the intl normalizer,
  • symfony/polyfill-mbstring for using the mbstring functions,
  • symfony/polyfill-util for using the polyfill utility helpers.
  • symfony/polyfill-uuid for using the uuid_* functions,

Requiring symfony/polyfill directly would prevent Composer from sharing correctly polyfills in dependency graphs. As such, it would likely install more code than required.

Design

This package is designed for low overhead and high quality polyfilling.

It adds only a few lightweight require statements to the bootstrap process to support all polyfills. Implementations are then loaded on-demand when needed during code execution.

If your project requires a minimum PHP version it is advisable to add polyfills for lower PHP versions to the replace section of your composer.json. This removes any overhead from these polyfills as they are no longer part of your project. The same can be done for polyfills for extensions that you require.

If your project requires php 7.0, and needs the mb extension, the replace section would look something like this:

{
    "replace": {
        "symfony/polyfill-php54": "*",
        "symfony/polyfill-php55": "*",
        "symfony/polyfill-php56": "*",
        "symfony/polyfill-php70": "*",
        "symfony/polyfill-mbstring": "*"
    }
}

Polyfills are unit-tested alongside their native implementation so that feature and behavior parity can be proven and enforced in the long run.

Download Details:

Author: Symfony
Source Code: https://github.com/symfony/polyfill 
License: MIT license

#php #symfony #component 

Polyfill: PHP Polyfills

Stopwatch: The Stopwatch Component Provides A Way to Profile Code

Stopwatch Component

The Stopwatch component provides a way to profile code.

Getting Started

$ composer require symfony/stopwatch
use Symfony\Component\Stopwatch\Stopwatch;

$stopwatch = new Stopwatch();

// optionally group events into sections (e.g. phases of the execution)
$stopwatch->openSection();

// starts event named 'eventName'
$stopwatch->start('eventName');

// ... run your code here

// optionally, start a new "lap" time
$stopwatch->lap('foo');

// ... run your code here

$event = $stopwatch->stop('eventName');

$stopwatch->stopSection('phase_1');

Resources

Download Details:

Author: Symfony
Source Code: https://github.com/symfony/stopwatch 
License: MIT license

#php #symfony #component 

Stopwatch: The Stopwatch Component Provides A Way to Profile Code
Rupert  Beatty

Rupert Beatty

1666830840

NavigationStack is A Stack-modeled UI Navigation Controller

NAVIGATION STACK

Navigation Stack is a library with stack-modeled UI navigation controller.


Requirements

  • iOS 9.0+
  • Xcode 9

Installation

Just add the Source folder to your project.

or use CocoaPods with Podfile:

pod 'Navigation-stack'

or Carthage users can simply add to their Cartfile:

github "Ramotion/navigation-stack"

Usage

YourNavigationController inherit from NavigationStack

add code to root viewViewController

override func viewDidLoad() {
    super.viewDidLoad()
    navigationController!.interactivePopGestureRecognizer?.delegate = self
  }
extension YourViewController: UIGestureRecognizerDelegate {
  func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {

    if navigationController?.viewControllers.count == 2 {
      return true
    }

    if let navigationController = self.navigationController as? NavigationStack {
      navigationController.showControllers()
    }

    return false
  }
}

Download Details:

Author: Ramotion
Source Code: https://github.com/Ramotion/navigation-stack 
License: MIT license

#swift #ios #library #component 

NavigationStack is A Stack-modeled UI Navigation Controller
Rupert  Beatty

Rupert Beatty

1666519560

Paper-switch: RAMPaperSwitch Is A Swift Material Design UI Module

PAPER SWITCH

A Swift material design UI module which paints over the parent view when the switch is on.


Requirements

  • iOS 8.0+
  • Xcode 9.0+

Installation

Just add the RAMPaperSwitch folder to your project.

or use CocoaPods with Podfile:

pod 'RAMPaperSwitch' 

or Carthage users can simply add to their Cartfile:

github "Ramotion/paper-switch"

Usage

RAMPaperSwitch is a drop-in replacement of UISwitch. You just need to set the onTintColor property of the switch, and it will automatically paint over its superview with the selected color. You have ability to set duration of animation instead of default value.

Create a new UISwitch in your storyboard or nib.

Set the class of the UISwitch to RAMPaperSwitch in your Storyboard or nib.

Set onTintColor for the switch

Set duration property programmatically if You want to change animation duration.

Add animation for other views near the switch if need.

Animate views

You can animate other views near the switch. For example, you can change color to views or labels that are inside the same superview. Duration of animation can be gotten from the RAMPaperSwitch's property duration. You can animate CoreAnimation properties like this:

self.paperSwitch.animationDidStartClosure = {(onAnimation: Bool) in
    UIView.transitionWithView(self.label, duration: self.paperSwitch.duration, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
        self.label.textColor = onAnimation ? UIColor.whiteColor() : UIColor.blueColor()
    }, completion:nil)
}

📱 Get the Showroom App for iOS to give it a try

Try this UI component and more like this in our iOS app. Contact us if interested.

 


Download Details:

Author: Ramotion
Source Code: https://github.com/Ramotion/paper-switch 
License: MIT license

#swift #switch #component 

Paper-switch: RAMPaperSwitch Is A Swift Material Design UI Module
Rupert  Beatty

Rupert Beatty

1666430520

Circle-menu: A Simple, Elegant UI Menu with A Circular Layout

CIRCLE MENU

Simple, elegant UI menu with a circular layout and material design animations


Requirements

  • iOS 9.0+
  • Xcode 9.0.1

Installation

Just add CircleMenuLib folder to your project.

or use CocoaPods with Podfile:

pod 'CircleMenu'

or Carthage users can simply add to their Cartfile:

github "Ramotion/circle-menu"

Usage

with storyboard

Create a new UIButton inheriting from CircleMenu

Add images for Normal and Selected state

Use delegate method to configure buttons

func circleMenu(circleMenu: CircleMenu, willDisplay button: UIButton, atIndex: Int)
  1. Use properties to configure CircleMenu
@IBInspectable var buttonsCount: Int = 3
@IBInspectable var duration: Double = 2 // circle animation duration
@IBInspectable var distance: Float = 100 // distance between center button and buttons

programmatically

let button = CircleMenu(
  frame: CGRect(x: 200, y: 200, width: 50, height: 50),
  normalIcon:"icon_menu",
  selectedIcon:"icon_close",
  buttonsCount: 4,
  duration: 4,
  distance: 120)
button.delegate = self
button.layer.cornerRadius = button.frame.size.width / 2.0
view.addSubview(button)

delegate methods

// configure buttons
optional func circleMenu(circleMenu: CircleMenu, willDisplay button: UIButton, atIndex: Int)

// call before animation
optional func circleMenu(circleMenu: CircleMenu, buttonWillSelected button: UIButton, atIndex: Int)

// call after animation
optional func circleMenu(circleMenu: CircleMenu, buttonDidSelected button: UIButton, atIndex: Int)

// call upon cancel of the menu - fires immediately on button press
optional func menuCollapsed(circleMenu: CircleMenu)

// call upon opening of the menu - fires immediately on button press
optional func menuOpened(circleMenu: CircleMenu)

🗂 Check this library on other language:

📱 Get the Showroom App for iOS to give it a try

Try this UI component and more like this in our iOS app. Contact us if interested.

 

Download Details:

Author: Ramotion
Source Code: https://github.com/Ramotion/circle-menu 
License: MIT license

#swift #ios #library #component 

Circle-menu: A Simple, Elegant UI Menu with A Circular Layout
Rupert  Beatty

Rupert Beatty

1666080984

An Animated Material Design UI Card Peek/pop Controller

EXPANDING COLLECTION

An animated material design UI card peek/pop controller


Requirements

  • iOS 9.0+
  • Xcode 9.0+

Installation

Just add the Source folder to your project.

or use CocoaPods with Podfile:

pod 'expanding-collection'

or Carthage users can simply add to their Cartfile:

github "Ramotion/expanding-collection"

Usage

import expanding_collection

Create CollectionViewCell

cell

Create UICollectionViewCell inherit from BasePageCollectionCell (recommend create cell with xib file)

Adding FrontView

  • add a view to YOURCELL.xib and connect it to @IBOutlet weak var frontContainerView: UIView!
  • add width, height, centerX and centerY constraints (width and height constranints must equal cellSize)

cell

  • connect centerY constraint to @IBOutlet weak var frontConstraintY: NSLayoutConstraint!
  • add any desired uiviews to frontView

Adding BackView

  • repeat step 2 (connect outlets to @IBOutlet weak var backContainerView: UIView!, @IBOutlet weak var backConstraintY: NSLayoutConstraint!)

Cell example DemoCell

If set tag = 101 for any FrontView.subviews this view will be hidden during the transition animation

Create CollectionViewController

Create a UIViewController inheriting from ExpandingViewController

Register Cell and set Cell size:

override func viewDidLoad() {
    itemSize = CGSize(width: 214, height: 460) //IMPORTANT!!! Height of open state cell
    super.viewDidLoad()

    // register cell
    let nib = UINib(nibName: "NibName", bundle: nil)
    collectionView?.registerNib(nib, forCellWithReuseIdentifier: "CellIdentifier")
}

Add UICollectionViewDataSource methods

extension YourViewController {

  override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return items.count
  }

  override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CellIdentifier"), forIndexPath: indexPath)
    // configure cell
    return cell
  }
}

Open Cell animation

override func viewDidLoad() {
    itemSize = CGSize(width: 214, height: 264)
    super.viewDidLoad()

    // register cell
    let nib = UINib(nibName: "CellIdentifier", bundle: nil)
    collectionView?.registerNib(nib, forCellWithReuseIdentifier: String(DemoCollectionViewCell))
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    cell.cellIsOpen(!cell.isOpened)
}

if you use this delegates method:

func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath)

func scrollViewDidEndDecelerating(scrollView: UIScrollView)

must call super method:

func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
  super.collectionView(collectionView: collectionView, willDisplayCell cell: cell, forItemAtIndexPath indexPath: indexPath)
  // code
}

func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
  super.scrollViewDidEndDecelerating(scrollView: scrollView)
  // code
}

Transition animation

Create a UITableViewController inheriting from ExpandingTableViewController

Set header height default 236

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    headerHeight = ***
}

OR

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    headerHeight = ***
}

Call the push method in YourViewController to YourTableViewController

  if cell.isOpened == true {
    let vc: YourTableViewController = // ... create view controller  
    pushToViewController(vc)
  }

For back transition use popTransitionAnimation()

🗂 Check this library on other language:

Download Details:

Author: Ramotion
Source Code: https://github.com/Ramotion/expanding-collection 
License: MIT license

#swift #library #component #animation 

An Animated Material Design UI Card Peek/pop Controller
Lawrence  Lesch

Lawrence Lesch

1664431680

Universal select/multiselect/tagging component for Vue.js

Vue-multiselect

Probably the most complete selecting solution for Vue.js 2.0, without jQuery.

Vue 3.0 Support

For Vue 3.0 compatible version see next branch.

Vue-Multiselect Screen

Features & characteristics:

  • NO dependencies
  • Single select
  • Multiple select
  • Tagging
  • Dropdowns
  • Filtering
  • Search with suggestions
  • Logic split into mixins
  • Basic component and support for custom components
  • V-model support
  • Vuex support
  • Async options support
  • > 95% test coverage
  • Fully configurable (see props list below)

Breaking changes:

  • Instead of Vue.partial for custom option templates you can use a custom render function.
  • The :key props has changed to :track-by, due to conflicts with Vue 2.0.
  • Support for v-model
  • @update has changed to @input to also work with v-model
  • :selected has changed to :value for the same reason
  • Browserify users: if you wish to import .vue files, please add vueify transform.

Install & basic usage

npm install vue-multiselect
<template>
  <div>
    <multiselect
      v-model="selected"
      :options="options">
    </multiselect>
  </div>
</template>

<script>
  import Multiselect from 'vue-multiselect'
  export default {
    components: { Multiselect },
    data () {
      return {
        selected: null,
        options: ['list', 'of', 'options']
      }
    }
  }
</script>

<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

JSFiddle

Example JSFiddle – Use this for issue reproduction.

Examples

in jade-lang/pug-lang

Single select / dropdown

multiselect(
  :value="value",
  :options="source",
  :searchable="false",
  :close-on-select="false",
  :allow-empty="false",
  @input="updateSelected",
  label="name",
  placeholder="Select one",
  track-by="name"
)

Single select with search

multiselect(
  v-model="value",
  :options="source",
  :close-on-select="true",
  :clear-on-select="false",
  placeholder="Select one",
  label="name",
  track-by="name"
)

Multiple select with search

multiselect(
  v-model="multiValue",
  :options="source",
  :multiple="true",
  :close-on-select="true",
  placeholder="Pick some",
  label="name",
  track-by="name"
)

Tagging

with @tag event

multiselect(
  v-model="taggingSelected",
  :options="taggingOptions",
  :multiple="true",
  :taggable="true",
  @tag="addTag",
  tag-placeholder="Add this as new tag",
  placeholder="Type to search or add tag",
  label="name",
  track-by="code"
)

addTag (newTag) {
  const tag = {
    name: newTag,
    code: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
  }
  this.taggingOptions.push(tag)
  this.taggingSelected.push(tag)
},

Asynchronous dropdown

multiselect(
  v-model="selectedCountries",
  :options="countries",
  :multiple="multiple",
  :searchable="searchable",
  @search-change="asyncFind",
  placeholder="Type to search",
  label="name"
  track-by="code"
)
  span(slot="noResult").
    Oops! No elements found. Consider changing the search query.
methods: {
  asyncFind (query) {
    this.countries = findService(query)
  }
}

Contributing

# serve with hot reload at localhost:8080
npm run dev

# distribution build with minification
npm run bundle

# build the documentation into docs
npm run docs

# run unit tests
npm run test

# run unit tests watch
npm run unit

For detailed explanation on how things work, checkout the guide and docs for vue-loader.

Documentation

Visit: vue-multiselect.js.org 

Download Details:

Author: Shentao
Source Code: https://github.com/shentao/vue-multiselect 
License: MIT license

#javascript #vue #select #component 

Universal select/multiselect/tagging component for Vue.js

Tool To Generate Different Types Of React Components From The Terminal

(Introduction article v1) 🛠WIP v2

How much time do you spend copying and pasting the component folder to create a new one ?
This is a tool to generate different types of React components from the terminal.

Available extension 

What you can do with this tool ?
 

create-component-app

Install

$ npm install -g create-component-app

Usage

$ cd ~/my-projects
$ create-component-app

Create your components guided from terminal with a lot of choices

  • Create different kind of components:
    • stateless
    • class
    • pure
    • custom
  • Set name of the new component
  • Integrate connect function of redux
  • Include an index file
  • Set a different component extension
    • js
    • jsx
  • Set a different style extension
    • css
    • scss
    • sass
    • less
  • Include a storybook file
  • Include a test file (with enzyme)
  • Set the destionation path of the new component

You can create a configuration file in your current project directory

Create-component-app uses cosmiconfig for configuration file support. This means you can configure cca via:

  • A .ccarc file, written in YAML or JSON, with optional extensions: .yaml/.yml/.json.
  • A cca.config.js file that exports an object.
  • A "cca" key in your package.json file.

The configuration file will be resolved starting from the root of your project, and searching up the file tree until a config file is (or isn't) found.

Basic Configuration

An example configuration file can be found here: .ccarc.example, you can use this file by copying it to the root of your project.

Currently supported options are:

OptionDescription
typeDefault type of the component ["stateless", "class", "pure"]
templatesDirPathDefault path to get the templates from the custom templates folder
pathDefault path to create component file and folder
jsExtensionDefault extension for your javascript file ["js", "jsx"]
cssExtensionDefault extension for your css file ["css", "scss", "sass", "less", false]. Set to false if you don't want a style file
includeTestsDefault flag to include a test file in the folder [true, false]
includeStoriesDefault flag to include a storybook file in the folder [true, false]
indexFileDefault flag to create an index file in the folder [false, true]
connectedDefault flag to integrate connect redux in the index file [false, true]
componentMethodsOnly for "class" and "pure", insert method inside the component (i.e. ["componentDidMount", "shouldComponentUpdate", "onClick"]). render and constructor will be always included.
fileNamesChoose the specific filename for your component's file. (COMPONENT_NAME will be replaced)
fileNames.testFileNamespecify the file name of your test file
fileNames.componentFileNamespecify the component file name
fileNames.styleFileNamespecify the style file name !!IMPORTANT: Include cssExtension.

You can also pass a config file

  1. Create a JSON file config.json:
  2. and pass the path to config param
$ create-component-app --config path/to/your/config.json

Passing a config file via the CLI overrides the configuration file loaded by cosmiconfig

You can pass params from the command line

$ create-component-app --path path/destionation

Passing a param via the CLI overrides the configuration file loaded by cosmiconfig

You can use your own custom templates

Simple steps to create your own templates docs/custom-templates

You can use templates from the community

Now, the first question that you receive is Do you wanna choose a template? if you answer yes, you can see the list of templates from the community.

create-component-app-templates

  • (Optional) Add to the settings templatesDirPath - a custom path to the user custom templates folder.
  • (Optional) Add to the settings templates - a list of used templates (with a default) to filter the list
  • (Optional) The user can choose between the available templates or use create-component-app -t templateName

Contributing

Now, the community can offer their templates! How?

Check the issue list to contribute on some activities or to advice new features! The library is open to everybody, contribute improve your skills.

create-component-app is maintained under the Semantic Versioning guidelines.

Use npm run watch while coding.

Contributors

Download Details:

Author: CVarisco
Source Code: https://github.com/CVarisco/create-component-app 
License: MIT license

#javascript #create #component #react #cli 

Tool To Generate Different Types Of React Components From The Terminal
Mike  Kozey

Mike Kozey

1661436723

Friendly_component: Package with A Friendly Component

TODO: Put a short description of the package here that helps potential users know whether this package might be useful for them.

Features

TODO: List what your package can do. Maybe include images, gifs, or videos.

Getting started

TODO: List prerequisites and provide or point to information on how to start using the package.

Usage

TODO: Include short and useful examples for package users. Add longer examples to /example folder.

const like = 'sample';

Additional information

TODO: Tell users more about the package: where to find more information, how to contribute to the package, how to file issues, what response they can expect from the package authors, and more.

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add friendly_component

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  friendly_component: ^0.0.2

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:friendly_component/friendly_component.dart';

example/lib/main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Download Details:

Author: Virakyuthpk
Source Code: https://github.com/virakyuthpk/friendly_component 
License: MIT license

#flutter #dart #component 

Friendly_component: Package with A Friendly Component

オートコンプリートを使用してカスタマイズ可能なReact検索コンポーネントを作成する

今日のほとんどのフロントエンドアプリケーションには、ある種の検索ボックスが必要です。これは、ユーザーがページ上で最初に操作するコンポーネントである場合があります。たとえば、Airbnb、Uber、Googleマップなどです。動作するだけでなく、ユーザーが目的のタスクを完了するようにガイドするのに十分な機能を備えた検索コンポーネントを作成することは、アプリケーションのユーザーエクスペリエンスにとって不可欠です。

Turnstoneは、React開発者がまさにそれを実行できるようにする新しいライブラリです。この軽量ライブラリ(12.2kB Gzip)には、オートコンプリート、自動キャッシング、WAI-ARIAアクセシビリティ、および機能的でアクセス可能な検索コンポーネントを構築できるその他の機能が付属しています。

ターンストーン要素は、CSSモジュールやTailwindCSSなどのさまざまなCSSメソッドを使用して簡単にカスタマイズできます。この記事では、TurnstoneとTailwindを使用して、マーベルコミックAPIにクエリを実行し、マーベルシネマティックユニバースに属するキャラクターを検索するアプリケーションを作成します。

GitHubでライブプロジェクトソースコードを表示します。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Reactフレームワークの基本的な理解
  • マーベルコミックAPIのAPIキー

Tailwind CSSの知識はありがたいですが、必須ではありません。

APIキーを取得してリファラーサイトを追加する

Marvel Developersサイトにアクセスして、新しいアカウントを登録してください。次に、公開APIキーと秘密APIキーが表示される[マイデベロッパーアカウント]に移動します。後で使用するために公開鍵をコピーします。

アプリがこのAPIにリクエストを送信する前に、そのドメイン名をリファラーサイトのリストに含める必要があります。同じページで、リファラーサイトのセクションまで下にスクロールし、アプリのドメイン名を追加しますlocalhost。アスタリスク*を使用してすべてのドメインからのリクエストを受け入れることもできますが、これは完全に安全というわけではありません。

これで、開発を開始する準備が整いました。

ターンストーンの機能

コンポーネントは、検索ボックスのTurnstoneさまざまな部分を制御するために使用されるさまざまなプロパティを受け入れます。コンポーネントのスタイル設定、検索ボックスのクエリ元のデータソース、エラーメッセージなど、すべてを適切なプロパティで構成できます。

このアプリケーションの構築に使用する重要なもののいくつかを見ていきましょう。

typeahead
タイプ:boolean
typeahead—別名オートコンプリート—は、ユーザーが入力している単語の残りの部分をアプリケーションが予測する機能です。これはtrueデフォルトで設定されています。

maxItems
タイプ:number
このプロパティは、に表示される検索結果の最大数を制御しますlistbox

listbox
次のように入力します:object、、arrayまたはfunction
listboxユーザーのクエリに応じて結果をレンダリングする方法を指定します。このプロパティは、データのソースと検索タイプ(またはのいずれstartsWithか)を制御しcontainsます。

オブジェクトとして、次のlistboxように単一のデータソースをクエリします。

const listbox = {
    displayField: 'characters',
    data: (query) =>
      fetch(`/api/characters?q=${query}`)
        .then(response => response.json()),
    searchType: 'startsWith',
}

return (
  <Turnstone listbox={listbox} />
)

data上記は、戻り値がアイテムの配列に解決されるPromiseである必要がある関数です。この関数は、現在のquery文字列を引数として受け取り、文字列が変更されるたびに再実行debounceWaitされます。そのため、、という別の小道具が必要になります。

アレイとして使用listboxする場合、複数のソースからデータを収集できます。

const listbox = [
  {
    id: 'cities',
    name: 'Cities',
    ratio: 8,
    displayField: 'name',
    data: (query) =>
      fetch(`/api/cities?q=${encodeURIComponent(query)}`)
        .then(response => response.json()),
    searchType: 'startswith'
  },
  {
    id: 'airports',
    name: 'Airports',
    ratio: 2,
    displayField: 'name',
    data: (query) =>
      fetch(`/api/airports?q=${encodeURIComponent(query)}`)
        .then(response => response.json()),
    searchType: 'contains'
  }
]

return (
  <Turnstone listbox={listbox} />
)

このシナリオでは、プロパティを使用して、に関連しratioて占める結果の数を指定できます。これは、たとえばが10に設定されている場合、各データソースからの数を合計して10にする必要があることを意味します。listboxmaxItemsmaxItemsratio

styles
タイプ:object
キーがTurnstoneによってレンダリングされた要素を表すオブジェクト。class対応する各値は、要素の属性を表す文字列です。

const styles = {
input: 'w-full h-12 border border-slate-300 py-2 pl-10',
  listbox: 'w-full bg-white sm:border sm:border-blue-300 sm:rounded text-left sm:mt-2 p-2 sm:drop-shadow-xl',
  groupHeading: 'cursor-default mt-2 mb-0.5 px-1.5 uppercase text-sm text-rose-300',
} 

return (
  <Turnstone styles={styles} />
)

Tailwindがいかに簡単に適合し、スタイリングプロセスを容易にするかがわかります。ドキュメントで利用可能なTurnstone要素のリストを表示します。

debounceWait
タイプ:number
このプロパティは、ユーザーが入力を終了してからクエリが関数に送信されるまでの待機時間をミリ秒単位で指定しますfetch

defaultListbox
このプロパティは同じですlistboxが、検索ボックスにフォーカスがある場合にクエリ文字列なしで表示されます。これは通常listbox、最近の検索用にを作成するために使用されます。

const defaultListBox = {
  displayField: 'Recent Searches',
  data: () => Promise.resolve(JSON.parse(localStorage.getItem('recentSearches')) || [])
}

return (
  <Turnstone defaultListBox={defaultListBox} />
)

アプリケーションの作成

ターミナルを開き、次のコマンドを使用して新しいReactアプリケーションを作成します。

npx create-react-app turnstone-demo

インストールが完了したら、プロジェクトのディレクトリに移動します。

cd turnstone-demo

そして、TurnstoneとTailwind CSSをインストールします—ピアの依存関係、PostCSS 、Autoprefixerと一緒に:

npm install -D turnstone tailwindcss postcss autoprefixer

APIキーの環境変数を作成することから始めましょう。プロジェクトのルートで、.envファイルを作成し、APIキーを保存します

// .env
REACT_APP_MARVEL_APIKEY = 'your_apikey_here'

REACT_APP_Create React Appは、必要なプレフィックスで作成された環境変数のサポートを提供します。この変数は、アプリ内でとしてアクセスできますprocess.env.REACT_APP_MARVEL_APIKEY

注意 :公開リポジトリでキーを公開しないように、ファイルに追加すること.env を忘れないでください。 .gitignore

画像の背景

プロジェクトのデモで見られる基になる画像の背景は、次のCSSクラスで作成されます。

// App.css
.image-backdrop {
  background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('../public/comic-backdrop.jpg');
  height: 100vh;
  width: 100%;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

bodyこのクラスをのタグに添付するpublic/index.htmlと、検索ボックスを配置するための画像の背景が必要になります。

// public/index.html
<!DOCTYPE html>
<html lang="en">
  <head> 
    <!-- markup -->
  </head>
  <body class="image-backdrop"> 
    <!-- more markup -->
    <div id="root"></div>
  </body>
</html>

追い風の初期化

Tailwind CSSを初期化するには、次のコマンドを実行します。

npx tailwindcss init -p

これにより、Tailwindの機能をカスタマイズおよび拡張するために使用されるファイルtailwind.config.jsとファイルが生成されます。postcss.config.js

今のところ、テンプレートパスを設定するだけです。tailwind.config.js以下のコードで更新します。

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

次に、ディレクティブをindex.css使用してTailwindレイヤーを追加します。@tailwind

// index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

これで、Tailwindのユーティリティクラスを使用してReactアプリのスタイリングを開始できます。まず、SearchBoxコンポーネントを画面の上部中央に配置します。

で、フォルダをsrc作成してファイルを保存します。次に、このコンポーネントをにインポートし、以下のTailwindクラスを親コンテナに適用します。componentsSearchBox.jsApp.js

// App.js
import SearchBox from './components/SearchBox'
import './App.css'

function App() {
  return (
    <div className='m-auto relative top-28 w-11/12 sm:w-6/12'>
      <SearchBox />
    </div>
  )
}
export default App

これにより、検索ボックスがページの上部中央に配置されます。

コンポーネントの使用Turnstone

検索ボックスの動的部分の構成を開始する前に、Turnstoneコンポーネントに次のプロパティを追加します。

// SearchBox.js
import Turnstone from 'turnstone'

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
    />
  )
}
export default SearchBox

clearButtonユーザーが検索ボックスに文字を入力するたびに、クリアボタンをレンダリングします。

autoFocustrueページの読み込み時に検索ボックスが自動的にフォーカスを受け取るように設定します。

maxItemsに表示される検索結果の最大数listboxを6に設定します。

listboxIsImmutabletrueの内容がlistboxクエリ間で変更されないように設定します。つまり、同じクエリで異なる結果を返すことはできません。

listboxそれでは、プロパティに移りましょう。

listbox

リストボックスのdataプロパティで、Comics APIにリクエストを送信し、プロセスで現在のクエリ文字列とAPIキーを添付します。

// SearchBox.js
import Turnstone from 'turnstone'

const SearchBox = () => {
  const listbox = {
    displayField: 'characters',
    data: async (query) => {
      const res = await fetch(
        `https://gateway.marvel.com:443/v1/public/characters?nameStartsWith=${query}&apikey=${process.env.REACT_APP_MARVEL_APIKEY}`
      )
      const data = await res.json()
      return data.data.results
    },
    searchType: 'startsWith',
  }

  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
    />
  )
}
export default SearchBox

Marvel APIには、使用可能なすべてのエンドポイントが一覧表示されたインタラクティブドキュメントページがあります。この例では、キャラクターのエンドポイントにリクエストを送信しました/v1/public/characters

、、、などstoriesの追加パラメータを追加して、さまざまな結果を得ることができます。また、パラメータを使用して、その値を文字列に設定します。eventsnameStartsWithnameStartsWithquery

この関数の結果は、名前が文字列resultsで始まるすべてのMarvel文字の配列を含むオブジェクトになります。query

// JSON result from API call. query="Doctor Strange"
{
  "code": 200,
  "status": "Ok",
  "copyright": "© 2022 MARVEL",
  "attributionText": "Data provided by Marvel. © 2022 MARVEL",
  "attributionHTML": "<a href=\"http://marvel.com\">Data provided by Marvel. © 2022 MARVEL</a>",
  "etag": "07a3a76164eec745484f34562db7ca7166c196cc",
  "data": {
    "offset": 0,
    "limit": 20,
    "total": 2,
    "count": 2,
    "results": [
      {
        "id": 1009282,
        "name": "Doctor Strange",
        "description": "",
        // ...

関連するデータはdata.results、関数の戻り値であるにあります。

この時点で、アプリケーションは正しく機能します。これで、Tailwindとstylesプロパティを使用してTurnstoneの要素のスタイルを設定できます。

Tailwindを使用したスタイリングTurnstone要素

styles前に説明したように、オブジェクトのキーは検索コンポーネントの特定の要素を表します。listbox、のハイライトされたアイテム、オートコンプリートテキストの色などの要素のスタイルを設定listboxして、見栄えの良い検索ボックスを作成できます。

// SearchBox.js
import Turnstone from 'turnstone'
import recentSearchesPlugin from 'turnstone-recent-searches'

const listbox = {
  // ...
}

const styles = {
  input: 'w-full border py-2 px-4 text-lg outline-none rounded-md',
  listbox: 'bg-neutral-900 w-full text-slate-50 rounded-md',
  highlightedItem: 'bg-neutral-800',
  query: 'text-oldsilver-800 placeholder:text-slate-600',
  typeahead: 'text-slate-500',
  clearButton:
    'absolute inset-y-0 text-lg right-0 w-10 inline-flex items-center justify-center bg-netural-700 hover:text-red-500',
  noItems: 'cursor-default text-center my-20',
  match: 'font-semibold',
  groupHeading: 'px-5 py-3 text-pink-500',
}

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
      styles={styles}
    />
  )
}

export default SearchBox

Itemコンポーネントプロップ

のプロパティをlistbox参照することでアイテムのスタイルを設定できますが、Turnstoneには、Turnstone要素の追加のカスタマイズとフォーマットを基本的に可能にするコンポーネントプロパティが用意されています。itemstyles

これを使用して、検索結果のキャラクター名の横にアバターを含める方法は次のとおりです。

// SearchBox.js
import Turnstone from 'turnstone'

const listbox = {
    // ...
}

const styles = {
    // ...
}

const Item = ({ item }) => {
  /* thubmnails from the API are stored as partials therefore 
    we have to concatenate the image path with its extension
  */
  const avatar = `${item.thumbnail.path}.${item.thumbnail.extension}`
  return (
    <div className='flex items-center cursor-pointer px-5 py-4'>
      <img
        width={35}
        height={35}
        src={avatar}
        alt={item.name}
        className='rounded-full object-cover mr-3'
      />
      <p>{item.name}</p>
    </div>
  )
}

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
      styles={styles}
      Item={Item}
    />
  )
}

export default SearchBox

最近の検索プラグイン

追加(1.7kB gzip)の場合、turnstone-recent-searchesTurnstoneのプラグインプロップに呼び出される別のパッケージを追加して、ユーザーの最近の検索を自動的に記録できます。

次のコマンドを使用して、このパッケージをインストールします。

npm install turnstone-recent-searches

そしてそれをそのpluginsように小道具に含めてください:

// SearchBox.js
import Turnstone from 'turnstone'
import recentSearchesPlugin from 'turnstone-recent-searches'

const styles = {
  input: 'w-full border py-2 px-4 text-lg outline-none rounded-md',
  listbox: 'bg-neutral-900 w-full text-slate-50 rounded-md',
  highlightedItem: 'bg-neutral-800',
  query: 'text-oldsilver-800 placeholder:text-slate-600',
  typeahead: 'text-slate-500',
  clearButton:
    'absolute inset-y-0 text-lg right-0 w-10 inline-flex items-center justify-center bg-netural-700 hover:text-red-500',
  noItems: 'cursor-default text-center my-20',
  match: 'font-semibold',
  groupHeading: 'px-5 py-3 text-pink-500',
}

const listbox = {
  displayField: 'characters',
  data: async (query) => {
    const res = await fetch(
      `https://gateway.marvel.com:443/v1/public/characters?nameStartsWith=${query}&apikey=${process.env.REACT_APP_MARVEL_APIKEY}`
    )
    const data = await res.json()
    return data.data.results
  },
  searchType: 'startsWith',
}

const Item = ({ item }) => {
  const avatar = `${item.thumbnail.path}.${item.thumbnail.extension}`
  return (
    <div className='flex items-center cursor-pointer px-5 py-4'>
      <img
        width={35}
        height={35}
        src={avatar}
        alt={item.name}
        className='rounded-full object-cover mr-3'
      />
      <p>{item.name}</p>
    </div>
  )
}

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
      styles={styles}
      Item={Item}
      plugins={[recentSearchesPlugin]}
    />
  )
}

export default SearchBox

この機能は、ユーザーのエクスペリエンスを向上させるため、同様に重要です。

結論

オートコンプリート検索ボックスは最新のUIデザインで普及しており、それらを簡単に実装するのに役立つReactライブラリがあることは素晴らしいことです。

Turnstoneのドキュメントは、API設計をうまく説明しており、徐々に学習曲線を示しています。これは、他のReactオートコンプリートライブラリを試したときはそうではありませんでした。ターンストーンの実際の例をもっと見るには、ターンストーンのウェブサイトで例をチェックしてください。

 このストーリーは、もともとhttps://blog.logrocket.com/create-customizable-react-search-component-autocomplete/で公開されました

#component #react 

オートコンプリートを使用してカスタマイズ可能なReact検索コンポーネントを作成する
Saul  Alaniz

Saul Alaniz

1655799240

Componente De Búsqueda De React Personalizable Con Autocompletar

La mayoría de las aplicaciones frontend actuales requieren un cuadro de búsqueda de algún tipo, que a veces es el primer componente con el que un usuario interactúa en su página, por ejemplo, Airbnb, Uber o Google Maps. La creación de un componente de búsqueda que no solo funcione, sino que sea lo suficientemente funcional para guiar a su usuario en la realización de la tarea deseada, es vital para la experiencia de usuario de su aplicación.

Turnstone es una nueva biblioteca que permite a los desarrolladores de React hacer precisamente eso. Esta biblioteca liviana (12.2kB Gzip) se envía con autocompletado, almacenamiento en caché automatizado, accesibilidad WAI-ARIA y otras funciones que le permiten crear un componente de búsqueda funcional y accesible.

Los elementos de Turnstone se pueden personalizar fácilmente utilizando varios métodos CSS, como módulos CSS o Tailwind CSS . En este artículo, usaremos Turnstone y Tailwind para crear una aplicación que consulte la API de Marvel Comics para buscar personajes pertenecientes al Universo Cinematográfico de Marvel.

Vea el proyecto en vivo y el código fuente en GitHub.

requisitos previos

Para seguir este tutorial, necesitará:

  • Comprensión básica del marco React
  • Una clave API de la API de Marvel Comics

El conocimiento de Tailwind CSS es apreciado, pero no obligatorio.

Obtener su clave API y agregar sitios de referencia

Dirígete al sitio de Marvel Developers y registra una nueva cuenta. Luego navegue a Mi cuenta de desarrollador donde encontrará sus claves API públicas y privadas. Copie la clave pública para más tarde.

Antes de que su aplicación pueda realizar una solicitud a esta API, su nombre de dominio debe incluirse en su lista de sitios de referencia. En la misma página, desplácese hacia abajo hasta la sección de sitios de referencia y agregue el nombre de dominio de su aplicación, es decir, localhost. También puede usar un asterisco *para aceptar solicitudes de todos los dominios, aunque esto no es del todo seguro.

¡Ahora ya está todo listo para comenzar a desarrollar!

características de vuelvepiedras

El Turnstonecomponente acepta una variedad de propiedades que se utilizan para controlar diferentes partes del cuadro de búsqueda. Todo, desde el estilo del componente, la fuente de datos de la que consulta el cuadro de búsqueda, los mensajes de error y más, se puede configurar con la propiedad adecuada.

Repasemos algunos de los más importantes que usaremos en la creación de esta aplicación.

typeahead
Tipo: boolean
typeahead- también conocido como autocompletar - es una función en la que una aplicación predice el resto de una palabra que está escribiendo un usuario. Esto está configurado truede forma predeterminada.

maxItems
Tipo: number
esta propiedad controla el número máximo de resultados de búsqueda que se muestran en el archivo listbox.

listbox
Escriba: object, arrayo function
listboxespecifica cómo se representan los resultados en respuesta a la consulta de un usuario. Esta propiedad controla el origen de los datos, así como el tipo de búsqueda, que podría ser startsWitho contains.

Como objeto, listboxconsulta una sola fuente de datos como tal:

const listbox = {
    displayField: 'characters',
    data: (query) =>
      fetch(`/api/characters?q=${query}`)
        .then(response => response.json()),
    searchType: 'startsWith',
}

return (
  <Turnstone listbox={listbox} />
)

dataarriba hay una función cuyo valor devuelto debe ser una Promesa que se resuelve en una matriz de elementos. Esta función toma la querycadena actual como argumento y se vuelve a ejecutar cada vez que cambia la cadena, razón por la cual necesitamos otra propiedad llamada debounceWait, más sobre eso momentáneamente.

Si se usa como una matriz, listboxpuede recopilar datos de múltiples fuentes:

const listbox = [
  {
    id: 'cities',
    name: 'Cities',
    ratio: 8,
    displayField: 'name',
    data: (query) =>
      fetch(`/api/cities?q=${encodeURIComponent(query)}`)
        .then(response => response.json()),
    searchType: 'startswith'
  },
  {
    id: 'airports',
    name: 'Airports',
    ratio: 2,
    displayField: 'name',
    data: (query) =>
      fetch(`/api/airports?q=${encodeURIComponent(query)}`)
        .then(response => response.json()),
    searchType: 'contains'
  }
]

return (
  <Turnstone listbox={listbox} />
)

En este escenario, ratiose puede utilizar una propiedad para especificar el número de resultados que ocupa listboxen relación con maxItems. Esto significa que, si maxItemsse establece en 10, por ejemplo, el rationúmero de cada fuente de datos debe sumar 10.

styles
Tipo: object
un objeto cuyas claves representan elementos representados por Turnstone. Cada valor correspondiente es una cadena que representa el classatributo del elemento .

const styles = {
input: 'w-full h-12 border border-slate-300 py-2 pl-10',
  listbox: 'w-full bg-white sm:border sm:border-blue-300 sm:rounded text-left sm:mt-2 p-2 sm:drop-shadow-xl',
  groupHeading: 'cursor-default mt-2 mb-0.5 px-1.5 uppercase text-sm text-rose-300',
} 

return (
  <Turnstone styles={styles} />
)

Podemos ver con qué facilidad se adapta Tailwind para facilitar el proceso de diseño. Vea la lista de elementos Turnstone disponibles en los documentos .

debounceWait
Tipo: number
esta propiedad especifica el tiempo de espera, en milisegundos, después de que el usuario termine de escribir antes de que su consulta se envíe a la fetchfunción.

defaultListbox
Esta propiedad es idéntica listboxpero se muestra cuando el cuadro de búsqueda está enfocado, sin una cadena de consulta. Suele utilizarse para crear un listboxpara búsquedas recientes:

const defaultListBox = {
  displayField: 'Recent Searches',
  data: () => Promise.resolve(JSON.parse(localStorage.getItem('recentSearches')) || [])
}

return (
  <Turnstone defaultListBox={defaultListBox} />
)

Crear la aplicación

Abre tu terminal y crea una nueva aplicación React con el siguiente comando:

npx create-react-app turnstone-demo

Una vez completada la instalación, navegue hasta el directorio del proyecto:

cd turnstone-demo

E instale Turnstone y Tailwind CSS, junto con sus dependencias de pares, PostCSS y Autoprefixer :

npm install -D turnstone tailwindcss postcss autoprefixer

Comencemos creando una variable de entorno para la clave API. En la raíz de su proyecto, cree un .envarchivo y almacene la clave API

// .env
REACT_APP_MARVEL_APIKEY = 'your_apikey_here'

Create React App brinda soporte para variables ambientales, que se crean con el REACT_APP_prefijo requerido. Luego se puede acceder a esta variable dentro de la aplicación como process.env.REACT_APP_MARVEL_APIKEY.

NB , recuerde agregar .env a su .gitignore archivo para que no exponga su clave en un repositorio público.

Fondo de imagen

El fondo de la imagen subyacente, como se ve en la demostración del proyecto, se crea con la siguiente clase CSS:

// App.css
.image-backdrop {
  background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('../public/comic-backdrop.jpg');
  height: 100vh;
  width: 100%;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

Adjunte esta clase a la bodyetiqueta public/index.htmly debería tener una imagen de fondo para colocar el cuadro de búsqueda:

// public/index.html
<!DOCTYPE html>
<html lang="en">
  <head> 
    <!-- markup -->
  </head>
  <body class="image-backdrop"> 
    <!-- more markup -->
    <div id="root"></div>
  </body>
</html>

Inicializando Tailwind

Para inicializar Tailwind CSS, ejecute el siguiente comando:

npx tailwindcss init -p

Esto genera los archivos tailwind.config.jsy postcss.config.js, que se utilizan para personalizar y ampliar las funciones de Tailwind.

Solo necesitamos configurar las rutas de la plantilla por ahora. Actualice tailwind.config.jscon el siguiente código:

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

A continuación, agregue las capas Tailwind para index.cssusar la @tailwinddirectiva:

// index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

Y ahora puede comenzar a diseñar su aplicación React con las clases de utilidad de Tailwind. Comencemos colocando el SearchBoxcomponente en la parte superior central de la pantalla.

En src, cree una componentscarpeta y almacene el SearchBox.jsarchivo. A continuación, importe este componente App.jsy aplique las siguientes clases de Tailwind al contenedor principal:

// App.js
import SearchBox from './components/SearchBox'
import './App.css'

function App() {
  return (
    <div className='m-auto relative top-28 w-11/12 sm:w-6/12'>
      <SearchBox />
    </div>
  )
}
export default App

Esto coloca el cuadro de búsqueda en la parte superior central de la página.

Usando el Turnstonecomponente

Antes de comenzar a configurar las partes dinámicas del cuadro de búsqueda, agregue las siguientes propiedades al Turnstonecomponente:

// SearchBox.js
import Turnstone from 'turnstone'

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
    />
  )
}
export default SearchBox

clearButtonmuestra un botón claro cada vez que el usuario ingresa un carácter en el cuadro de búsqueda.

autoFocusestablecido en truehace que el cuadro de búsqueda reciba automáticamente el foco cuando se carga la página.

maxItemsestablece el número máximo de resultados de búsqueda que se mostrarán en listbox6.

listboxIsImmutableestablecido en trueasegura que el contenido de listboxno cambie entre consultas; es decir, la misma consulta no puede devolver resultados diferentes.

Ahora pasemos a la listboxpropiedad.

listbox

En la datapropiedad de listbox, hacemos una solicitud a la API de Comics, adjuntando la cadena de consulta actual y su clave de API en el proceso:

// SearchBox.js
import Turnstone from 'turnstone'

const SearchBox = () => {
  const listbox = {
    displayField: 'characters',
    data: async (query) => {
      const res = await fetch(
        `https://gateway.marvel.com:443/v1/public/characters?nameStartsWith=${query}&apikey=${process.env.REACT_APP_MARVEL_APIKEY}`
      )
      const data = await res.json()
      return data.data.results
    },
    searchType: 'startsWith',
  }

  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
    />
  )
}
export default SearchBox

La API de Marvel tiene una página de documentación interactiva donde se enumeran todos los puntos finales disponibles. En nuestro caso, hemos realizado una solicitud al extremo de caracteres: /v1/public/characters.

Se pueden agregar parámetros adicionales como stories, eventso para obtener resultados diferentes. nameStartsWithTambién usamos el nameStartsWithparámetro, estableciendo su valor en la querycadena.

El resultado de esta función debería ser un objeto que contenga una resultsmatriz de todos los personajes de Marvel cuyo nombre comience con la querycadena:

// JSON result from API call. query="Doctor Strange"
{
  "code": 200,
  "status": "Ok",
  "copyright": "© 2022 MARVEL",
  "attributionText": "Data provided by Marvel. © 2022 MARVEL",
  "attributionHTML": "<a href=\"http://marvel.com\">Data provided by Marvel. © 2022 MARVEL</a>",
  "etag": "07a3a76164eec745484f34562db7ca7166c196cc",
  "data": {
    "offset": 0,
    "limit": 20,
    "total": 2,
    "count": 2,
    "results": [
      {
        "id": 1009282,
        "name": "Doctor Strange",
        "description": "",
        // ...

Los datos relevantes se encuentran en data.results, que es el valor de retorno de la función.

En este punto, la aplicación funciona correctamente. Ahora podemos proceder a diseñar los elementos de Turnstone con Tailwind y la stylespropiedad.

Elementos de diseño Turnstonecon Tailwind

Como se explicó anteriormente, las claves en el stylesobjeto representan un cierto elemento del componente de búsqueda. Podemos diseñar elementos como listbox, elementos resaltados en listboxe incluso el color del texto de autocompletar para crear un cuadro de búsqueda más atractivo:

// SearchBox.js
import Turnstone from 'turnstone'
import recentSearchesPlugin from 'turnstone-recent-searches'

const listbox = {
  // ...
}

const styles = {
  input: 'w-full border py-2 px-4 text-lg outline-none rounded-md',
  listbox: 'bg-neutral-900 w-full text-slate-50 rounded-md',
  highlightedItem: 'bg-neutral-800',
  query: 'text-oldsilver-800 placeholder:text-slate-600',
  typeahead: 'text-slate-500',
  clearButton:
    'absolute inset-y-0 text-lg right-0 w-10 inline-flex items-center justify-center bg-netural-700 hover:text-red-500',
  noItems: 'cursor-default text-center my-20',
  match: 'font-semibold',
  groupHeading: 'px-5 py-3 text-pink-500',
}

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
      styles={styles}
    />
  )
}

export default SearchBox

Itemaccesorio de componente

Aunque podemos diseñar elementos listboxhaciendo referencia a la itempropiedad en styles, Turnstone proporciona propiedades de componentes que esencialmente permiten una personalización y formato adicionales de los elementos de Turnstone.

Así es como podemos usar esto para incluir un avatar junto al nombre del personaje en un resultado de búsqueda:

// SearchBox.js
import Turnstone from 'turnstone'

const listbox = {
    // ...
}

const styles = {
    // ...
}

const Item = ({ item }) => {
  /* thubmnails from the API are stored as partials therefore 
    we have to concatenate the image path with its extension
  */
  const avatar = `${item.thumbnail.path}.${item.thumbnail.extension}`
  return (
    <div className='flex items-center cursor-pointer px-5 py-4'>
      <img
        width={35}
        height={35}
        src={avatar}
        alt={item.name}
        className='rounded-full object-cover mr-3'
      />
      <p>{item.name}</p>
    </div>
  )
}

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
      styles={styles}
      Item={Item}
    />
  )
}

export default SearchBox

Complemento de búsquedas recientes

Por un extra (gzip de 1.7kB), turnstone-recent-searchesse puede agregar otro paquete llamado al complemento de Turnstone para registrar automáticamente las búsquedas recientes del usuario.

Instale este paquete con el siguiente comando:

npm install turnstone-recent-searches

E inclúyelo en el pluginsprop como tal:

// SearchBox.js
import Turnstone from 'turnstone'
import recentSearchesPlugin from 'turnstone-recent-searches'

const styles = {
  input: 'w-full border py-2 px-4 text-lg outline-none rounded-md',
  listbox: 'bg-neutral-900 w-full text-slate-50 rounded-md',
  highlightedItem: 'bg-neutral-800',
  query: 'text-oldsilver-800 placeholder:text-slate-600',
  typeahead: 'text-slate-500',
  clearButton:
    'absolute inset-y-0 text-lg right-0 w-10 inline-flex items-center justify-center bg-netural-700 hover:text-red-500',
  noItems: 'cursor-default text-center my-20',
  match: 'font-semibold',
  groupHeading: 'px-5 py-3 text-pink-500',
}

const listbox = {
  displayField: 'characters',
  data: async (query) => {
    const res = await fetch(
      `https://gateway.marvel.com:443/v1/public/characters?nameStartsWith=${query}&apikey=${process.env.REACT_APP_MARVEL_APIKEY}`
    )
    const data = await res.json()
    return data.data.results
  },
  searchType: 'startsWith',
}

const Item = ({ item }) => {
  const avatar = `${item.thumbnail.path}.${item.thumbnail.extension}`
  return (
    <div className='flex items-center cursor-pointer px-5 py-4'>
      <img
        width={35}
        height={35}
        src={avatar}
        alt={item.name}
        className='rounded-full object-cover mr-3'
      />
      <p>{item.name}</p>
    </div>
  )
}

const SearchBox = () => {
  return (
    <Turnstone
      id='search'
      name='search'
      autoFocus={true}
      typeahead={true}
      clearButton={true}
      debounceWait={250}
      listboxIsImmutable={true}
      maxItems={6}
      noItemsMessage="We couldn't find any character that matches your search"
      placeholder='Search for any character in the MCU'
      listbox={listbox}
      styles={styles}
      Item={Item}
      plugins={[recentSearchesPlugin]}
    />
  )
}

export default SearchBox

Esta característica es igualmente importante ya que crea una mejor experiencia para su usuario.

Conclusión

Los cuadros de búsqueda de autocompletar prevalecen en el diseño moderno de la interfaz de usuario, y tener una biblioteca React que nos ayude a implementarlos fácilmente es excelente.

La documentación de Turnstone hace un buen trabajo al explicar el diseño de su API, dándole una curva de aprendizaje gradual, que no fue el caso cuando probé otras bibliotecas de autocompletado de React. Para ver más ejemplos de Turnstone en acción, consulte los ejemplos en el sitio web de Turnstone .

 Esta historia se publicó originalmente en https://blog.logrocket.com/create-customizable-react-search-component-autocomplete/

#component #react 

Componente De Búsqueda De React Personalizable Con Autocompletar

Reactコンポーネント– Reactで検索、フィルター、ページネーションコンポーネントを作成する

ちょうど1年前に「Reactでコンポーネントを検索してフィルタリングする方法」という記事を書きました。

それ以来、多くの変化がありました。チュートリアルで使用したAPIが機能しなくなったため、この記事では、コンポーネントにページ付けを導入しながら、以前の例を再作成します。

このチュートリアルではReact.jsを使用しているため、従うにはReactとJavaScriptの基本を理解する必要があります。このチュートリアルは、前の記事「 Reactでコンポーネントを検索およびフィルタリングする方法」を読んだことも前提としています。

入門

このチュートリアルの国データを取得するには、CountryAPI.io国APIを使用します。

APIを使用するにはAPIキーが必要です。APIキーを取得するには、CountryAPI.ioにアクセスしてアカウントを作成します。APIキーがダッシュボードに表示されます。

Web-capture_4-6-2022_184858_countryapi.io--1-

次に、CreateReactAppを使用して新しいReactアプリを作成します。これを行うには、ターミナルで次を実行します。

# Run this to use npm
npx create-react-app search-app
# Or run this to use yarn
yarn create react-app search-app
cd my-app
npm start
# Or with yarn
yarn start

いつものように、ライブプレビューでは、Codepenを使用してすべての例を表示します。

データを取得する方法

https://countryapi.io/api/allデータを取得するには、APIキーを指定しながらエンドポイントに対してGET呼び出しを行う必要があります。以前に作成したReactアプリのsrc > App.jsファイルで、既存のコードをすべて削除し、次のように置き換えます。

import { useState, useEffect } from "react";
import "./App.css";

function App() {
  const [error, setError] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [items, setItems] = useState([]);
  
   useEffect(() => {
    const request_headers = new Headers();
    const api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
    request_headers.append("Authorization", `Bearer ${api_key}`);
    request_headers.append("Content-Type", "application/json");

    const request_options = {
      method: "GET",
      headers: request_headers,
    };

    fetch("https://countryapi.io/api/all", request_options)
      .then((res) => res.json())
      .then(
        (result) => {
          setLoaded(true);
          setItems(result);
        },
        (error) => {
          setLoaded(true);
          setError(error);
        }
      );
  }, []);
  
  console.log(items)
  
  if (error) {
    return <>{error.message}</>;
  } else if (!loaded) {
    return <>loading...</>;
  } else {
     return (
     //
    );
  }
 }

export default App;

上記で行ったのは、JavaScriptフェッチAPIを使用してエンドポイントにGETリクエストを行い、返されたデータをitemsを使用して状態に保存することだけsetState(result)でした。

データの表示方法

次に、APIデータを表示する必要があります。これは、APIによって返されるすべての国のリストになります。

リストを作成するには、返されたオブジェクトの値からオブジェクトの配列を生成する必要があります。src> App.jsファイルを開き、次のコードを追加します。

import { useState, useEffect } from "react";
import "./App.css";

function App() {
  const [error, setError] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [items, setItems] = useState([]);

  useEffect(() => {
    // fetch data
  }, []);

  const data = Object.values(items);

  if (error) {
    return <>{error.message}</>;
  } else if (!loaded) {
    return <>loading...</>;
  } else {
    return (
      <div className="wrapper">
        <ul className="card-grid">
          {data.map((item) => (
            <li key={item.alpha3Code}>
              <article className="card">
                <div className="card-image">
                  <img src={item.flag.large} alt={item.name} />
                </div>
                <div className="card-content">
                  <h2 className="card-name">{item.name}</h2>
                  // other card content
              </article>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default App;

CSSを追加すると、上記の例は以下のプレビューのようになります。

検索コンポーネントを作成する方法

まず、ユーザーが検索クエリを入力できる入力フィールドを作成します。src > App.jsを開き、次の編集を行います。

...

function App() {
 const [query, setQuery] = useState("");
 
  if (error) {
    return <>{error.message}</>;
  } else if (!loaded) {
    return <>loading...</>;
  } else {
    return (
      <div className="wrapper">
        <div className="search-wrapper">
          <label htmlFor="search-form">
            <input
              type="search"
              name="search-form"
              id="search-form"
              className="search-input"
              placeholder="Search for..."
              onChange={(e) => setQuery(e.target.value)}
            />
            <span className="sr-only">Search countries here</span>
          </label>
        </div>
        
        ...
      </div>
    );
  }
}

export default App;

上記で入力フィールドを作成し、onChangeイベントハンドラーを使用して、入力フィールドの値が変更されるたびに、フックをquery使用するように値を設定しました。useState

次に、それを使用しqueryてAPIから返されるデータをフィルタリングする必要があります。

const search_parameters = Object.keys(Object.assign({}, ...data));

 function search(data) {
    return items.filter(
      (item) =>
        search_parameters.some((parameter) =>
          item[parameter].toString().toLowerCase().includes(query)
        )
    );
  }

これを少し分解してみましょう。まず、引数としてsearch()取り入れる関数を作成しました。検索パラメータのいずれかにクエリの値が含まれているかどうかを確認したメソッドとメソッドをdata組み合わせます。Array.filter()Array.some()includes(query)

もちろん、検索パラメータをハードコーディングすることもできます。

const search_parameters = ["Capital", "Name", ...]

これは最速の方法ですが、将来を保証するものではありません(APIから返されるデータは変更される可能性があります)–これは難しい方法であることがわかりました。dataしたがって、ハードコーディングする代わりに、APIから返されたものから検索パラメーターを取得できます。

const search_parameters = Object.keys(Object.assign({}, ...data));

最後に、search(data)関数から返された新しいデータを使用して国リストを作成する必要があります。App.jsファイルを開き、前に作成したリストを編集します。

...
{search(data).map((item) => (
 <li key={item.alpha3Code}>
  <article className="card">
   <div className="card-image">
    <img src={item.flag.large} alt={item.name} />
     </div>
   <div className="card-content">
    <h2 className="card-name">{item.name}</h2>
    ...           
   </div>
   </article>
 </li>
))}

検索機能が追加されると、ライブプレビューは次のようになります。

フィルタコンポーネントを作成する方法

フィルタは、特定のキーワードでデータをグループ化するためによく使用されます。この例では、地域ごとにデータをグループ化します。

繰り返しますが、これをハードコーディングする代わりに、データからリージョンを取得できます。

const filter_items = [...new Set(data.map((item) => item.region))];

作成した検索入力フィールドの後に、フィルターUI(HTMLselect入力フィールド)を追加します。

...
const [filter, setFilter] = useState("");
...

<div className="select">
 <select
  onChange={(e) => setFilter(e.target.value)}
  className="custom-select"
  aria-label="Filter Countries By Region">
  <option value="">Filter By Region</option>
  {filter_items.map((item) => (
   <option value={item}>Filter By {item}</option>
  ))}
</select>
<span className="focus"></span>
</div>

フィルタを追加するには、search(data)関数を変更する必要があります。したがって、検索したデータのみを返すのではなく、フィルターパラメーターとしてデータを返すようになりました。

function search(items) {
    return items.filter(
      (item) =>
        item.region.includes(filter) &&
        search_parameters.some((parameter) =>
          item[parameter].toString().toLowerCase().includes(query)
        )
    );
  }

これで、国を地​​域でフィルタリングできるようになりました。ライブプレビューと完全なコードはCodepenで見つけることができます:

ページネーションコンポーネントを作成する方法

ページ付けは、Webページに表示されるアイテムのリストを減らすだけでなく、ページの読み込み時にユーザーがダウンロードする必要のあるリソースが少ないため、アプリのパフォーマンスも向上します。

国データのページネーションを作成するには、まず、表示するアイテムの数を指定しますuseState

const [paginate, setpaginate] = useState(8);

次に、paginate値を使用して国リストを更新しましょう。

...
{search(data)
 .slice(0, paginate)
 .map((item) => (
 <li key={item.alpha3Code}>
  <article className="card">
   <div className="card-image">
    <img src={item.flag.large} alt={item.name} />
     </div>
   <div className="card-content">
    <h2 className="card-name">{item.name}</h2>
    ...           
   </div>
   </article>
 </li>
))}

次に、この状態を呼び出すたびにこの状態を更新する関数を作成する必要があります。

const load_more = (event) => {
  setpaginate((prevValue) => prevValue + 8);
};

load_more最後に、クリックしたときに関数を呼び出すボタンを作成しましょう。

<button onClick={load_more}>Load More</button>

繰り返しますが、ページネーションのプレビューはCodePenで見つけることができます:

結論

この記事では、CountryAPI.ioを使用して実際のアプリを構築することにより、Reactで検索、フィルター、およびページ付け機能を実装する方法について説明しました。

これで素晴らしいものを作成した場合は、遠慮なくツイートして、@sprucekhalifaのタグを付けてください。そして、フォローボタンを押すことを忘れないでください。

ハッピーコーディング!

このストーリーは、もともとhttps://www.freecodecamp.org/news/how-to-react-components/で公開されました

#react-native #component 

Reactコンポーネント– Reactで検索、フィルター、ページネーションコンポーネントを作成する