simple-react-ui
Version:
a simple react component library written in TypeScript+ React.js
204 lines (172 loc) • 8.11 kB
Markdown
### 前置步骤:
* 把UEditor相应的文件包放置在浏览器端可以访问的URL路径下,比如,`/static`
* 引入 simple-react-ui 的 UEditor 组件
这样仅仅是配置好前端;后端仍然需要编写代码,可以使用[express-ueditor](https://www.npmjs.com/package/express-ueditor)充当后端
## 如何使用
`UEditor`提供两种模式供用户使用:
### 非受控模式
在非受控模式下,用户主要通过
* `initialContent` 属性来提供初始值
* `afterInit(ue)` 回调函数来与 `UEditor` 互动,其中`ue`参数是`UE.getEditor('id')`返回的编辑器实例。
`afterInit(ue)`在某种程度上类似于原生`React`组件的`ref`回调,我们可以把`ue`实例传递给父组件保存起来,从而可以在父组件中来做任何原生`UEditor`可以做的事儿。比如当某个按钮被点击,调用`this.ue.getContent()`获取当前值。
注意,用户不得指定`value`属性,否则会自动转换为 *受控模式*
### 受控模式
受控模式是更符合`React`理念使用方式。注意, *受控模式* 和 *非受控模式* 不能共存,二者只巨其一。
在受控模式下,用户可以通过
* `value`属性:父组件可以通过`props.value`属性来动态设置编辑器的内容
* `onChange(content)` 可选的事件处理函数,当编辑器的内容发生变化之后可以选择以`onChange(content)`的方式通知父组件。
* 受控模式下:当父组件改变本组件的`props.value`,或者因为人手工输入内容,导致编辑器内容区发生变化(调用`ue#setContent(content)`)完成之后所触发的事件。
* 在非受控模式下:其实没必要使用该钩子,因为设定编辑器内容的`ue.setContent()`都是手工调用的。
注意在受控模式下不要指定`props.initialContent`属性——它会在组件加装完成后被`props.value`属性覆盖掉,也就是说,编辑器中实际的初始化内容是`props.value`。
## 示例一:以非受控模式使用
```js
<form id="postAddOrEditForm">
<input name='title' type='text' placeholder='标题'
value={this.state.title||''}
onChange={(v)=>{ this.setState({title:v.target.value}); }}
/>
<textarea required placeholder='摘要'
value={this.state.excerpt||''}
onChange={(v)=>{ this.setState({excerpt:v.target.value});}}
/>
<UEditor id="ueditorContainer" name="content"
initialContent={this.props.initialContent}
width={800} height={500}
afterInit={(ue)=>{
const id=this.props.id;
if(!!id){ // 编辑已有文章的表单
// 获取最新的数据
fetch(`/post/detail?id=${id}`,{/**/})
.then(resp=>resp.json())
.then(info=>{
const state=Object.assign({},info);
this.setState(state,()=>{
ue.setContent(info.content);
});
});
}else{
// 这是一个用于新增文章的表单
}
}}
/>
<Button onClick={e=>{
e.preventDefault();
const ue=this.ue;
let content=ue.getContent();
// ... ajax post to server
return fetch('',{/**/})
.then(resp=>resp.json())
.then((info)=>{
message.info(`创建文章成功!`);
ue.setContent('');
});
}}>提交
</Button>
</form>
```
## 示例二:以受控模式使用
在受控模式下,需要指定`props.value`以令组件接受来自父组件指定的内容:
示例一:
```javascript
class AddOrEditForm extends React.Component{
render(){
// 某个编辑或者
<UEditor id="ueditorContainer" name="content"
width={800} height={200}
afterInit={(ue)=>{
const id=this.props.id;
if(!!id){ // 当前是在编辑模式
// 获取最新的数据
return model.methods.detail(id)
.then(info=>{
const state=Object.assign({},info);
this.setState(state,()=>{
ue.setContent(info.description);
});
});
}else{ /*当前是新增模式*/ }
}}
value={this.state.description}
onChange={content=>{
this.setState({
description: content,
});
}}
/>
}
}
```
上例中,我们做了以下两件事:
1. 通过`props.value`迫使`UEditor`组件接受来自父组件的内容,
2. 通过`onChange()`迫使`UEditor`组件内容在人工输入导致的内容变化后会同步到父状态内
这种操作方式和`React`的`input`非常类似对不对?
```javascript
class XForm extends React.Component{
render(){
<input value={this.state.content} onChange={v=>{ /* ... */}} />
}
}
```
不过,受控组件的精华并不在此,我们可以通过编写一个函数,自动绑定`onChange`来做数据同步!作为受控模式的示例,这里配合 `ant-design`的`Form.create()()` 使用:
```javascript
export class PlainAddOrEditForm extends React.Component{
constructor(props){
super(props);
this.state= {
title:'',
categoryId:'',
featureImageUrl:'#',
keywords:[
{id:null,postId:null,tag:''},
],
commentable:true,
};
}
render() {
const FormItem=Form.Item;
const {getFieldDecorator,getFieldsError, getFieldError, isFieldTouched,validateFields}=this.props.form;
const hasFieldError=(fieldname)=>isFieldTouched(fieldname) && getFieldError(fieldname);
const hasErrors=(fieldsError)=>Object.keys(fieldsError).some(field => fieldsError[field]);
return (
<Form onSubmit={e=>{
e.preventDefault();
validateFields((err, values) => {
if (!err) {
console.log(values);
}
});
}}>
<FormItem label='标题' validateStatus={hasFieldError('title')} help={hasFieldError('title')||''} >
{
getFieldDecorator('title',{
rules:[{required:true,message:'title required'}],
})(
<Input name='title' type='text' placeholder='标题'/>
)
}
</FormItem>
<FormItem label='content' validateStatus={hasFieldError('content')} help={hasFieldError('content')||''} >
{
getFieldDecorator('content',{
rules:[{required:true,message:'content required'}],
initialValue:'<p>测试</p><b>测试</b>'
})(
<UEditor id="ueditorContainer"
width={800} height={500}
uconfigSrc={"/url/to/uconfig.js"} ueditorSrc={"/url/to/ueditor.js"}
afterInit={(ue)=>{ this.ue=ue;}}
onChange={content=>{ console.log(content); }}
/>
)
}
</FormItem>
<FormItem>
<Button htmlType='submit' type="primary" size="large" disabled={hasErrors(getFieldsError())}>Submit</Button>
</FormItem>
</Form>
);
}
}
export default Form.create()(PlainAddOrEditForm);
```
注意这里的`UEditor`,我们可以继续监听`onChange(content)`事件,不过没必要再手工同步状态了!