Misunderstanding the use of selection

Hello. I am trying to understand how selection works and what it is for. The documentation is very scarce for use cases and examples of use.
As I understand it, this is a selected area of doc that will be changed by the transaction. But then why doesn’t the following code work?

const editorState = EditorState.create({
  doc: "Hello world",
  extensions: [
    EditorState.allowMultipleSelections.of(true)
  ],
});

const editorView = new EditorView({
  state: editorState,
  parent: domNode,
});

//....

let transaction = editorState.update(
  {
    selection: EditorSelection.create([EditorSelection.range(0, 4)]),
  },
  editorState.replaceSelection('!')
);

editorView.dispatch(transaction);
// Expect "! world", get "!Hello world"

editorState.replaceSelection will replace that state’s selection. That call has no way to know about the other object passed to update (and more generally, changes passed to update all conceptually happen at once, starting from the initial state).

What are you trying to do? It is rarely necessary to set a new selection at the start of a transaction—if you’re doing something to the user’s current selection, that’ll already be there. If you want to change some specific part of the document, you can generate the required changes directly, without involving the selection.

Thanks marjin. I’m just trying to figure out where and how I can use the ‘selection’. For which cases ‘selection’ is needed.
In the example from the documentation, when initializing the component, we set the ‘selection’ and then in the transaction we do the replacement of characters in this selection.

import {EditorState, EditorSelection} from "@codemirror/state"

let state = EditorState.create({
  doc: "hello",
  selection: EditorSelection.create([
    EditorSelection.range(0, 4),
    EditorSelection.cursor(5)
  ]),
  extensions: EditorState.allowMultipleSelections.of(true)
})
console.log(state.selection.ranges.length) // 2

let tr = state.update(state.replaceSelection("!"))
console.log(tr.state.doc.toString()) // "!o!"

I am trying to implement the same case by means of an update, without setting selections during initialization.

Right, but why? If you really need to do this you could dispatch two transactions, one setting the selection and one replacing it, but I’m not really familiar with any situations where that’s useful.