【问题标题】:draft.js: text editor populate value from other component's stateDraft.js:文本编辑器从其他组件的状态填充值
【发布时间】:2021-02-23 16:11:17
【问题描述】:

我正在使用draft.js 制作文本编辑器,我有两个组件:CreatePost.js 从后端获取帖子字段并使用用户输入填充状态,TextEditor.js 包含文本编辑器我在CreatePost.js 中使用它。文本编辑器应以CreatePost.js onChange 的状态填充body 字段。

我的问题是如何让文本编辑器填充其他组件中的状态?我需要使用道具吗?

之前,我在CreatePost.js 中有一个文本区域,其中填充了body。我希望其他组件中的文本编辑器来填充它。我试过使用 <TextEditor onChange={this.changeHandler} value={body} />CreatePost.js 中,但它不起作用。

console.log(body):

posts.js(控制器)

exports.create = (req, res) => {
  const { title, body, date } = req.body;
  const post = new Post({
    title,
    body,
    date,
    "author.id": req.profile._id,
    "author.name": req.profile.name,
  });
  post
    .save()
    .then((response) => {
      res.send(response);
    })
    .catch((err) => {
      return res.status(400).json({
        error: errorHandler(err),
      });
    });
};

CreatePost.js

class CreatePost extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      title: "",
      body: "",
      createdPost: "",
      error: "",
    };
  }

  changeHandler = (e) => {
    this.setState({ [e.target.name]: e.target.value });
  };

  submitHandler = (e) => {
    e.preventDefault();
    const {
      user: { _id },
    } = isAuthenticated();
    axios({
      url: `${API}/post/new-post/${_id}`,
      method: "POST",
      data: this.state,
    })
      .then((response) => {
        this.setState({ createdPost: this.state.title });
        return response;
      })
      .catch((error) => {
        if (!this.state.title || !this.state.body) {
          this.setState({
            error: "This post must contain a title and a body.",
          });
        }
        console.log(error);
      });
  };

...

  render() {
    const { title, body } = this.state;
    return (
      <>
        <Navbar />
        <Tabs>
          <TabList className="tabs">
            <Tab className="tab">Draft</Tab>
            <Tab className="tab">Preview</Tab>
          </TabList>
          <TabPanel>
            <div className="newpost_container">
              <form className="newpost_form" onSubmit={this.submitHandler}>
                <div className="form-group">
                  <input
                    type="text"
                    placeholder="Title"
                    name="title"
                    className="newpost_field newpost_title"
                    onChange={this.changeHandler}
                    value={title}
                  />
                </div>
                <div className="form-group newpost_body">
                <TextEditor />
                </div>
                <button className="btn publish-post-btn" type="submit">
                  Publish
                </button>
                {this.showSuccess()}
                {this.showError()}
              </form>
            </div>
          </TabPanel>

          <TabPanel>
            <div>
              <h1>{title}</h1>
              <div>{body}</div>
            </div>
          </TabPanel>
        </Tabs>
      </>
    );
  }
}

export default CreatePost;

TextEditor.js

class TextEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      editorState: EditorState.createEmpty(),
    };
    this.plugins = [addLinkPlugin];
  }
  toggleBlockType = (blockType) => {
    this.onChange(RichUtils.toggleBlockType(this.state.editorState, blockType));
  };

  onChange = (editorState) => {
    this.setState({
      editorState,
    });
  };

  handleKeyCommand = (command) => {
    const newState = RichUtils.handleKeyCommand(
      this.state.editorState,
      command
    );
    if (newState) {
      this.onChange(newState);
      return "handled";
    }
    return "not-handled";
  };

// onClick for format options

  onAddLink = () => {
    const editorState = this.state.editorState;
    const selection = editorState.getSelection();
    const link = window.prompt("Paste the link -");
    if (!link) {
      this.onChange(RichUtils.toggleLink(editorState, selection, null));
      return "handled";
    }
    const content = editorState.getCurrentContent();
    const contentWithEntity = content.createEntity("LINK", "MUTABLE", {
      url: link,
    });
    const newEditorState = EditorState.push(
      editorState,
      contentWithEntity,
      "create-entity"
    );
    const entityKey = contentWithEntity.getLastCreatedEntityKey();
    this.onChange(RichUtils.toggleLink(newEditorState, selection, entityKey));
  };

  toggleBlockType = (blockType) => {
    this.onChange(RichUtils.toggleBlockType(this.state.editorState, blockType));
  };

  render() {
    return (
      <div className="editorContainer">
        <div className="toolbar">
          <BlockStyleToolbar
            editorState={this.state.editorState}
            onToggle={this.toggleBlockType}
          />
          // format buttons
        </div>

        <div>
          <Editor
            placeholder="Post Content"
            blockStyleFn={getBlockStyle}
            editorState={this.state.editorState}
            handleKeyCommand={this.handleKeyCommand}
            onChange={this.onChange}
            plugins={this.plugins}
            placeholder="Post Content"
          />
        </div>
      </div>
    );
  }
}

export default TextEditor;

【问题讨论】:

标签: reactjs state draftjs


【解决方案1】:

看起来您实际上已经非常接近解决这个问题了。使用道具向TextEditor 发送更改处理程序时,您走在正确的道路上。解决问题的一种方法是将editorState 向上移动到CreatePost 组件,然后向下传递值和更改处理程序。如果您这样做,您应该从 TextEditor 文件中删除 editorState 和它的更改处理程序。只需继续您的示例,这样的事情应该可以工作,我还没有尝试过代码,但它应该可以帮助您朝着正确的方向前进。

CreatePost.js

constructor(props) {
    super(props);
    this.state = {
      title: "",
      body: EditorState.createEmpty(),
      createdPost: "",
      error: "",
    };
}

....

<TextEditor onChange={(value) => this.setState({ body: value })} editorState={body} />

TextEditor.js

<Editor
  placeholder="Post Content"
  blockStyleFn={getBlockStyle}
  editorState={this.props.editorState}
  handleKeyCommand={this.handleKeyCommand}
  onChange={this.props.onChange}
  plugins={this.plugins}
  placeholder="Post Content"
/>

发布数据时,我们需要访问编辑器的内容,而不是EditorState。我们可以通过 Draft.js API 做到这一点(在此处查看更多信息:https://draftjs.org/docs/api-reference-editor-state/#getcurrentcontent)。不幸的是,这还不够。我们还需要将内容转换为更易于处理的格式。我们可以使用您还需要从库 (https://draftjs.org/docs/api-reference-data-conversion/#converttoraw) 导入的 draft.js convertToRaw 来做到这一点。转换为原始返回一个 JS 对象,因此我们还需要将其转换为字符串,然后才能使用 JSON.stringify() 将其发送到服务器。

axios({
  url: `${API}/post/new-post/${_id}`,
  method: "POST",
  data: {
    ...this.state,
    body: JSON.stringify(convertToRaw(this.state.body.getCurrentContent()))
  }
})

【讨论】:

  • 感谢您的回答。我已经实现了代码,它有很大帮助!尽管文本格式有效,但不幸的是,body 仍然不包含该值,因此该帖子未保存到数据库中。我用console.log(body) 的输出更新了我的问题。
  • 好吧,这很好!我明白。我认为“新”问题是因为我们没有从编辑器获得实际的文本内容,我们实际上只获得了editorState,这有点不同。我添加了更多代码以及如何获取编辑器内容的示例。我认为最好在请求中执行,而不是直接在状态中执行此操作。我们仍然需要将editorState 传递给编辑器,否则它将停止工作。
  • 感谢您更新您的答案!不幸的是,这并没有改变任何东西。我没有任何错误,但我收到了404,当我console.log(body) 时输出是一样的。
  • 对不起,我浏览文档的速度有点过快。实际上,我们需要进行两次额外的转换,然后才能将数据转换为可以发送的格式。首先我们需要使用convertToRaw,它是draft.js 中的一个函数。之后,我们需要使用JSON.stringify() 将数据转换为字符串。那应该行得通。您可能需要在您的服务器上做一些事情,并且数据仍将与 Draft.js 格式相关。关于404,这可能是由错误的数据格式引起的。 console.log(body) 仍然是错误的,因为我们没有更新 body 状态。
  • 谢谢!该请求现在返回 200 并且 body 被持久化到数据库中,但是,它返回包装在字符串中的整个对象,例如:{"blocks":[{"key":"1r6jb","text":"aaaaaaaaa","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}} 而不仅仅是文本。我尝试更改服务器中的代码,但我不明白我需要更改/添加什么。我已经用 server.js 中的controller 更新了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
  • 2016-10-30
  • 2014-11-06
  • 2021-11-11
  • 2018-11-27
  • 2019-08-19
相关资源
最近更新 更多