ソースは以下
https://github.com/ichi944/with-validation/tree/master/src
動機
- react-redux でいい感じのバリデーションはないか?
- redux-form -> 覚えることがたくさんありそう(特に他のUIライブラリとの連携など)
- 上記の理由からコンポーネントとして実装されているやつはやめたい
- Laravelに馴染みがあるのでバリデーションルールを同じ感じで書きたい
- VueにはVee-Validateという使いやすいやつがある
- validatorjsというライブラリがあるがインターフェースがいまいちreactっぽくない
- HOCで穏便すればいいのでは
実装
かいつまんでいうとwithValidatorでFormをラップすることによって
- Form側にdirty, errorsというメンバーが生える
- makeAllDirty(), makeValidator()メソッドが生える
- HOCするときにオプションを2つ渡す
- setData: propsからチェックする値を取り出す関数
- rules: validatorjsに渡すバリデーションルール文字列
- HOC内のcomponentDidUpdate時に対象の値が変更されていればバリデーションを行いFormにerrorsとdirtyを渡す
- Formはerrorsとdirtyの値で適当にメッセージを表示したりなどする
- SubmitのときはmakeValidator()でvalidatorjsのインスタンスが取得できるのでそれでなんとかする
//... 省略
import withValidator from './withValidator';
//... 省略
// Define Form
class Form extends Component {
//... 省略
render() {
const { dirty, errors, email, password, handleChange } = this.props;
const styles = {
error: {
color: 'red',
fontSize: '0.8em',
},
};
return (
<div>
<div>
<label>email: </label>
<input
type="text"
name="email"
value={email}
onChange={handleChange}
/>
{ dirty.email && errors.has('email') ?
<span style={styles.error}>{errors.first('email')}</span> : null }
</div>
<div>
<label>password: </label>
<input
type="password"
name="password"
value={password}
onChange={handleChange}
/>
{ dirty.password && errors.has('password') ?
<span style={styles.error}>{errors.first('password')}</span> : null }
</div>
<div><button onClick={this.handleSubmit}>送信</button></div>
</div>
);
}
}
// Wrapp with Validator
const FormWithValidator = withValidator({
setData: ({email, password}) => ({
email,
password,
}),
rules: {
email: 'required',
password: 'required|min:8',
},
})(Form);
// Connect to Store
const mapStateToProps = ({email, password}) => ({
email,
password,
});
const mapDispatchToProps = (dispatch) => ({
handleChange(e) {
const { name, value } = e.target;
dispatch({
type: 'UPDATE_FORM',
name,
value,
});
}
});
const ConnectedForm = connect(mapStateToProps, mapDispatchToProps)(FormWithValidator);
// Mount
class App extends Component {
render() {
return (
<div>
<h1>Validator Form</h1>
<ConnectedForm />
</div>
);
}
}
render(
<Provider store={store}><App /></Provider>,
document.querySelector('#app')
);
残りはコード参照。
もっと色々できそうではあるのですが、窮屈になってきそうなのでここまでにとどめています。