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

相关文章
相关标签/搜索