Formik官方應用案例解析(三)使用react-select

react-select簡介

React-Select是github上一個極其火的控件庫,星數達到13004,它是React開發中幾乎是你必需打交道的一個內容。React Select的基本功能實現的是一個表單中的常見的下拉列表框控件,其實它的功能擴展來看遠遠不止如此,它支持:

  • 多選

  • 樣式定製

  • 多級聯動選擇

  • 異步加載

    等等。
    但是,如果在你的開發中使用的是一個很基礎性的下拉列表框,那麼你可以直接使用類似於Semantic UI或者是Material React控件庫的Select組件,甚至是最基本的HTML5組件中的那個。

值得注意的是,如如今react-select組件從1.0升級到2.0,變化了巨大變化。有關細節請參考官方網站,總起來看越升級越容易使用,功能也越強大。

在Formik中使用react-select組件

Formik官方也提供了一個使用react-select組件的基本例子,但是使用的是react-select組件的Ver 1.x。在1.x版本時期,組件的樣式是使用css方案定義的,但是升級到2.0後,樣式使用了更爲先進且更迎合React開發思想的Emotion這個開源庫(使用JSX組件思想——CSS-in-JS——開發樣式)。由於Formik官方提供的相關實例極爲簡單,所以幾需要作兩處修改即可。
工程名稱:formik-09x-react-select-example,主要文件:index.js
修改後完整代碼如下:

import './formik-demo.css';
import React from 'react';
import { render } from 'react-dom';
import { withFormik } from 'formik';
//ERROR NOW: import Yup from 'yup';==>changed into the following
import * as Yup from 'yup';

import Select from 'react-select';
//NOT SUPPORTED IN VERSION 2.X.
// Styles are now implemented with css-in-js rather than less / scss stylesheets
//import 'react-select/dist/react-select.css';

// Helper styles for demo
import './formik-demo.css';
import {
    MoreResources,
    DisplayFormikState,
} from './formik-helper';

import SimpleSelect from './SimpleSelect'
import AnimatedMulti from './AnimationMultiSelect'

const formikEnhancer = withFormik({
    mapPropsToValues: props => ({
        email: '',
        topics: []
    }),
    validationSchema: Yup.object().shape({
        email: Yup.string()
            .email('Invalid email address')
            .required('Email is required!'),
        topics: Yup.array()
            .min(3, 'Pick at least 3 tags')
            .of(
                Yup.object().shape({
                    label: Yup.string().required(),
                    value: Yup.string().required(),
                })
            ),
    }),
    handleSubmit: (values, { setSubmitting }) => {
        const payload = {
            ...values,
            topics: values.topics.map(t => t.value),
        };
        setTimeout(() => {
            alert(JSON.stringify(payload, null, 2));
            setSubmitting(false);
        }, 1000);
    },
    displayName: 'MyForm'
});

const MyForm = props => {
  const {
    values,
    touched,
    dirty,
    errors,
    handleChange,
    handleBlur,
    handleSubmit,
    handleReset,
    setFieldValue,
    setFieldTouched,
    isSubmitting,
  } = props;
  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email" style={{ display: 'block' }}>
        Email
      </label>
      <input
        id="email"
        placeholder="Enter your email"
        type="email"
        value={values.email}
        onChange={handleChange}
        onBlur={handleBlur}
      />
      {errors.email &&
      touched.email && (
        <div style={{ color: 'red', marginTop: '.5rem' }}>
          {errors.email}
        </div>
      )}
      <MySelect
        value={values.topics}
        onChange={setFieldValue}
        onBlur={setFieldTouched}
        error={errors.topics}
        touched={touched.topics}
      />
      <button
        type="button"
        className="outline"
        onClick={handleReset}
        disabled={!dirty || isSubmitting}
      >
        Reset
      </button>
      <button type="submit" disabled={isSubmitting}>
        Submit
      </button>

      <DisplayFormikState {...props} />
    </form>
  );
};

const options = [
  { value: 'Food', label: 'Food' },
  { value: 'Being Fabulous', label: 'Being Fabulous' },
  { value: 'Ken Wheeler', label: 'Ken Wheeler' },
  { value: 'ReasonML', label: 'ReasonML' },
  { value: 'Unicorns', label: 'Unicorns' },
  { value: 'Kittens', label: 'Kittens' },
];

class MySelect extends React.Component {
  handleChange = value => {
    // this is going to call setFieldValue and manually update values.topcis
    this.props.onChange('topics', value);
  };

  handleBlur = () => {
    // this is going to call setFieldTouched and manually update touched.topcis
    this.props.onBlur('topics', true);
  };

  render() {
    return (
      <div style={{ margin: '1rem 0' }}>
        <label htmlFor="color">
          Topics (select at least 3){' '}
        </label>
        <Select
          id="color"
          options={options}
          isMulti
          onChange={this.handleChange}
          onBlur={this.handleBlur}
          value={this.props.value}
        />
        {!!this.props.error &&
        this.props.touched && (
          <div style={{ color: 'red', marginTop: '.5rem' }}>
            {this.props.error}
          </div>
        )}
      </div>
    );
  }
}

const MyEnhancedForm = formikEnhancer(MyForm);

const App = () => (
  <div className="app">
    <h1>
      Using{' '}
      <a href="https://github.com/jaredpalmer/formik">
        Formik
      </a>{' '}
      with 3rd-party input components
    </h1>
    <p>
      This example shows to use Formik with a 3rd-party
      input component. The trick is to use Formik's{' '}
      <code>setFieldValue</code> prop and a custom component
      class whenever you need a custom change handler.{' '}
    </p>
    <p>
      To show this off, below is a Formik-enhanced form. It
      has a "vanilla" Formik input for <code>email</code>{' '}
      and a custom select component for <code>topics</code>{' '}
      that uses Jed Watson's {' '}
      <a href="https://github.com/JedWatson/react-select">
        react-select
      </a>{' '}
      library.
    </p>
    <MyEnhancedForm />
      <hr/>
      <SimpleSelect/>
      <hr/>
      <AnimatedMulti/>
      <hr/>
    <MoreResources />
  </div>
);

render(<App />, document.getElementById('root'));

第一處修改是屏蔽css文件的導入引用,如下:
//import 'react-select/dist/react-select.css';
第二處更爲簡單,只需要把<Select/>組件中的屬性表達方式修改一下,即把multi={true}修改爲isMulti就可以了,如下:

        <Select
          id="color"
          options={options}
          isMulti
          onChange={this.handleChange}
          onBlur={this.handleBlur}
          value={this.props.value}
        />

另外還添加了兩個我自己加入的react-select組件,分別是SimpleSelect和AnimationMultiSelect,它們各自的源碼如下,請參考:

//SimpleSelect.js

import React,{Component} from 'react'
import Select from 'react-select'

const options=[
    {value:'liu',label:'劉備'},
    {value:'guan',label:'關羽'},
    {value:'zhang',label:'張飛'}
]
const SimpleSelect=()=>(
    <Select options={options}/>
)
export default SimpleSelect

//AnimationMultiSelect

import React from 'react';

import Select from 'react-select';
import makeAnimated from 'react-select/lib/animated';
// import { colourOptions } from '../data';
const colourOptions=[
    {value:'c1',label:'劉備'},
    {value:'c2',label:'關羽1'},
    {value:'c3',label:'關羽2'},
    {value:'c4',label:'關羽3'},
    {value:'c5',label:'張飛'}
]

export default function AnimatedMulti() {
    return (
        <Select
            closeMenuOnSelect={false}
            components={makeAnimated()}
            defaultValue={[colourOptions[4], colourOptions[5]]}
            isMulti
            options={colourOptions}
        />
    );
}

引用

1,https://react-select.com/home#getting-started
2,https://blog.csdn.net/Tony1590112/article/details/78296345
3,https://github.com/emotion-js/emotion
4,https://github.com/jaredpalmer/formik

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章