Preface
Let's not talk about any grammatical principles. First, we simulate the grammatical usage forcibly according to the API effect, and realize a simple version of React.
render
The first step is to create an element to return with the class and bind the click event. The code is as follows. You can see a button appears normally.
class AddButton { createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } render() { this.dom = this.createDOM(`<button>0</button>`); this.dom.addEventListener("click", () => console.log("click"), false); return this.dom; } } document.body.appendChild(new AddButton().render());
state && setState
Implementing Class State and Modifying State Method
class AddButton { constructor() { this.state = { num: 0 }; } createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } setState(state) { this.state = state; this.dom = this.render(); } handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } render() { this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`); this.dom.addEventListener("click", () => this.handleAdd(), false); console.log(this.dom); return this.dom; } } document.body.appendChild(new AddButton().render());
After rendering, you can see that the DOM output has been changed, but the view has not been rendered, because this is a one-time insertion of the end, so let's go down to render the view.
Rendering
Now we build the operation of inserting data into the class and add a new method to insert new elements to remove the old ones.
class AddButton { constructor() { this.state = { num: 0 }; } createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } changeDom() { const oDom = this.dom; this.dom = this.render(); document.body.insertBefore(this.dom, oDom); document.body.removeChild(oDom); } setState(state) { this.state = state; this.changeDom(); } handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } render() { this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`); this.dom.addEventListener("click", () => this.handleAdd(), false); return this.dom; } } document.body.appendChild(new AddButton().render());
Now the effect is achieved, but you still have to start by manually inserting elements into the view
Extracting public classes
Let's first extract some common methods into a single class
class Component { constructor() {} createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } changeDom() { const oDom = this.dom; this.dom = this._render(); this.wrapper.insertBefore(this.dom, oDom); this.wrapper.removeChild(oDom); } setState(state) { this.state = state; this.changeDom(); } _render(wrapper) { if (wrapper) this.wrapper = wrapper; this.dom = this.createDOM(this.render()); this.dom.addEventListener("click", () => this.handleAdd(), false); return this.dom; } }
Then the component simply inherits the Component and processes its own logic.
class AddButton extends Component { constructor() { super(); this.state = { num: 0 }; } handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } render() { return `<button id="btn">${this.state.num}</button>`; } }
Another question is whether the click event is temporarily coupled into Component, not to mention it.
ReactDom.render
Everyone knows that React provides a way to insert components into a specified element, which we simulate directly.
const ReactDom = { render(component, wrapper) { wrapper.appendChild(component._render(wrapper)); } }; ReactDom.render(new AddButton(), document.getElementById("root"));
Props
Another important implementation of data transmission is as follows
const ReactDom = { render(component, wrapper) { wrapper.appendChild(component._render(wrapper)) } } class Component { constructor(props = {}) { this.props = props } createDOM(domString) { const div = document.createElement("div") div.innerHTML = domString return div } changeDom() { const oDom = this.dom this.dom = this._render() this.wrapper.insertBefore(this.dom, oDom) this.wrapper.removeChild(oDom) } setState(state) { this.state = state this.changeDom() } _render(wrapper) { if (wrapper) this.wrapper = wrapper this.dom = this.createDOM(this.render()) this.dom.addEventListener("click", () => this.handleAdd(), false) return this.dom } } class AddButton extends Component { constructor(props) { super(props) this.state = { num: 0 } } handleAdd() { const num = this.state.num + 1 this.setState({ num: num }) } render() { console.log(this.props) return `<button id="btn">${this.state.num}</button>` } } ReactDom.render(new AddButton({a:1}), document.getElementById("root"))
So far, we have simply simulated the general grammar implementation of React, regardless of the actual thinking.