facebookarchive / draft-js

A React framework for building text editors.
https://draftjs.org/
MIT License
22.57k stars 2.64k forks source link

Do simple, working examples for draft.js methods exist anywhere? #2157

Closed jkhaui closed 5 years ago

jkhaui commented 5 years ago

Hi,

I've spent the past 4 days reading draft.js documentation, playing with code samples, etc. and by this stage, all I can really do is echo the sentiments of another user:

"Everything looks like a rocket-science. It's so crazy, tricky and hard to understand. Please, simplify it somehow.

Some code even doesn't work itself.

...I tried to console.log(editorState), but it shows some crazy, messy, unreadable and huge structure."

I understand web-based RTE is extremely complex and it's amazing what has already been accomplished with draft.js... but at the same time I thought the point of libraries/frameworks + open-source was to make it simple for others to build abstraction layers.

E.g I made my first goal to create a replace text function the "draft.js way" using Modifier.replaceText. Not only have I failed at this, but my search for examples returned only one semi-relevant post from 2016 (whose solution also didn't work). I can't tell if my code is wrong, the methods I'm using are outdated, or if there's a bug in draft.js itself... simply because up-to-date examples don't exist anywhere

Surely I am missing something? How can working examples for a project this big be almost non-existent? 😕 Many thanks if someone can point me in the right direction!

robbertbrak commented 5 years ago

@jkhaui: Have you looked at https://github.com/nikgraf/awesome-draft-js? Some of the standalone editors, plugins and utilities appear pretty much up to date.

How can working examples for a project this big be almost non-existent?

I think that Draft.js suffers from the fact that Facebook allocates almost no resources to maintaining this project, since they consider it to be "very stable" (https://github.com/facebook/draft-js/issues/1193#issuecomment-408123192), while at the same time they don't allow anyone from outside Facebook to become a maintainer. It appears that @claudiopro is the current maintainer, so he might be able to elaborate on my outsider perspective.

But don't despair! Even though it takes some time to get used to the way things work with Draft.js, it is very flexible, and the goal you have set for yourself is certainly possible to achieve.

jkhaui commented 5 years ago

@robbertbrak thanks a lot for the response — makes me realise I'm not going crazy!

Yeah, I've been through awesome-draft-js. There's some great projects like Draftail and scholar-draft and I actually got helpful examples from their codebase. It's strange Facebook barely makes an effort on maintenance, especially for simple things like fixing sloppy grammar in the draft.js documentation. This project deserves a more professional image.

I agree. I need to invest more time to learn draft.js properly, I underestimated its learning curve :)

folkjc commented 5 years ago

A tip-- Draft is based on a library named Immutable.js, as mentioned in the docs. It's worth learning how to use that library (just the basics are necessary, like Maps, Sets), since most of the methods are derived from that library.

For example, if you want to see the EditorState's currentContent, you can't type editorState.currentContent like standard JS allows, but you need to type editorState.getCurrentContent().

jkhaui commented 5 years ago

Thank you @jf97! I'm embarrassed to say I did not even know that. Will definitely look into it.

jkhaui commented 5 years ago

After hours of searching, I finally solved the ostensibly simple task of replacing occurrences of a word/string while a user is typing. Thought I'd share the code a) in case others find it helpful; and b) to provide a specific example of the need for much better documentation.

// Function to replace occurrences of ` foo ` with ` bar `.
replaceString = (chars, editorState) => {
    const contentState = editorState.getCurrentContent();
    const selectionState = editorState.getSelection();
    const block = contentState.getBlockForKey( selectionState.getAnchorKey() );
    const FOO = ' foo ';
    const BAR = ' bar ';

    // Detect a match. Can be substituted with a RegEx test condition.
    if ( block.getText().indexOf( FOO ) !== -1 ) {
        const currentSelectionState = this.state.editorState.getSelection();

        const newContentState = Modifier.replaceText(
            contentState,
            // The text to replace, which is represented as a range with a start & end offset.
            selectionState.merge( {
                // The starting position of the range to be replaced.
                anchorOffset: currentSelectionState.getEndOffset() - FOO.length,
                // The end position of the range to be replaced.
                focusOffset: currentSelectionState.getEndOffset()
            } ),
            // The new string to replace the old string.
           BAR
        );

        this.setState( {
            editorState: EditorState.push(
                editorState,
                newContentState,
                'replace-text'
            )
        } )
    }
}

Then pass replaceString to the handleBeforeInput prop of the Editor component:

        <Editor
            editorState={ this.state.editorState }
            onChange={ this.onChange }
            handleBeforeInput={ this.replaceString }
            ref={ this.editorContainer }
        />

The draft.js docs make no mention at all of the need to call selectionState.merge, and the example is lacking crucial information, to say the least: https://draftjs.org/docs/api-reference-modifier#replacetext

thibaudcolas commented 5 years ago

@jkhaui I share your general sentiment but I think you should be careful not to underestimate the complexity of something seemingly simple like replaceString. For example, if you’re replacing text as the user types, you most likely don’t want the user to be able to undo the replacement – and the replacement should happen right after they type something to be replaced (I think yours might be lagging a few character behind since you’re not using chars from handleBeforeInput). You might also want to do this when people paste as well as type. Or have both operations work slightly differently – e.g. be undo-able by typing but not by copy-pasting. Then you also need to think of what happens to styles / links if replacing text these are be applied on.

Generally the Draft.js API is quite low-level, which makes it possible to build pretty arbitrary rich text interactions, but that flexibility also comes at the cost of having to know quite a lot to build seemingly simple interactions.

It also doesn’t help that the project has very poor documentation beyond the API reference. As @robbertbrak mentions there is very little maintenance happening on the project from Facebook, and no maintainers from outside Facebook, so the documentation hasn’t been getting much love.

Anyway, if you’re wanting to learn more Draft.js by examples I would recommend looking through everything on https://github.com/nikgraf/awesome-draft-js. In particular I learned a lot from https://github.com/jpuri/draftjs-utils, because of its "single-purpose utility" nature. I have a similar "DraftUtils" file in my project that might also be interesting for the same purpose: https://github.com/springload/draftail/blob/master/lib/api/DraftUtils.js.

jkhaui commented 5 years ago

Thanks for your great work on Draftail, @thibaudcolas and I do find those utils files very helpful. Actually, I was going to make a request/proposal for you, but I see you're taking a break from Patreon. It's probably appropriate I close this issue and contact you elsewhere