
### 前置步骤：

* 把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)`事件，不过没必要再手工同步状态了！