前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Hooks】:[组]Declarative & Imperative

【Hooks】:[组]Declarative & Imperative

作者头像
WEBJ2EE
发布2021-02-26 16:12:04
6630
发布2021-02-26 16:12:04
举报
文章被收录于专栏:WebJ2EEWebJ2EE
代码语言:javascript
复制
目录
1. Declarative vs Imperative
2. Declarative vs Imperative Programming
3. React Hooks are a More Accurate Implementation of the React Mental Model
  3.1. Using React's initial class-based implementation of state and effects 
  3.2. Using React's new hook-based implementation of state and effects 
4. StackOverflow:Declarative vs. Imperative
5. Declarative Programming & React 
  5.1. Declarative Programming 
  5.2. Declarative programming in React  

1. Declarative vs Imperative

This article strives to make this distinction of declarative vs imperative specifically for people transitioning from vanilla Javascript into React. Additionally, I am writing this during a similar transition so I apologize for any misinformation. But, I am a person who cannot just idly sit by with “React is a declarative writing style.” Therefore, I will attempt to pull apart what makes React declarative and compare it to its imperative counterpart.

First off, from a very high level, declarative programming focuses on the WHAT rather than the HOW. What does this mean? Declarative programming is much more driven by the result and describing this end result rather than the step by step process of getting to the result (often disregarding the process to get there). This is opposed to imperative programming which is much more instructional and cares about the step by step process. As per every “Declarative vs Imperative” article, here is a directions metaphor of getting to Penn Station from Port Authority:

The imperative programming paradigm: “After exiting Port Authority, head southwest on 8th Ave toward W 41st St, turn left onto W 33rd St and you should arrive on Penn Station.”

The declarative programming paradigm: “Penn Station is located at 7th & 8th Avenues, between 31st & 33rd Streets New York, NY 10001”

As you can see, the first example lists out the directions step by step, hence indicative of an imperative approach. The second example merely describes the end location/result with an address, disregarding how the traveler/user might get there. To really illustrate this distinction, let’s jump into a simple coding example through Ruby before we get into React and Javascript. Here are two approaches to implementing a simple, linear search algorithm in Ruby:

代码语言:javascript
复制
//Imperative 
def imperative_search(array, item)
  for i in array do
    if i == item
      return item
    end
  end
  return false
end
代码语言:javascript
复制
//Declarative
def declarative_search(array, item)
  array.find(item)
end

The first is a prime example of the imperative search as it lays out each step of how the search function works and how it got to the result. This really illustrates the HOW and gives discrete ‘instructions’ to get to the desired result. In contrast, the declarative example focuses on purely the result and describing what this result will look like. If you have been working with Ruby’s enumerator methods such as each, collect, select etc., guess what? You’ve been writing declarative code this whole time!

Declarative React

Finally, we’ll move into the crux of this blog: why React is declarative. If you are like me, the Javascript you’ve written so far has been of an imperative style. For instance, take this example of very imperative code which manipulates the DOM by adding a div and an h1 with my favorite artist “Mitski” to the body:

代码语言:javascript
复制
function addArtistNameToBody() {
  const bodyTag = document.querySelector('body')
  const divTag = document.createElement('div')
  let h1Tag = document.createElement('h1')
  h1Tag.innerText = "Mitski"
  divTag.append(h1Tag)
  bodyTag.append(divTag)
}

This is very imperative as, via step by step instructions, it finds the necessary tags and appends the necessary elements. In contrast, to achieve the same result through react would look something like this:

代码语言:javascript
复制
class Artist extends Component {
  render() {
    return(
      <div>
        <h1>{this.props.name}</h1>
      </div>)
  }
}

As you can see, the React way focuses on the result and further describes it in the render block of code. Simply put, “what I want rendered on the page is going to look like this and I don’t care about how you get there.” Referring to the metaphor, “This is my address. This is how my house looks like. I don’t care how you get here but get here!” Therefore, React’s implementation is declarative because you state in code the result, what you ultimately want displayed on the page, and not the instructions or steps to get there.

Before researching and writing this blog, my apprehension of the declarative programming paradigm mainly came from lack of distinction between declarative and functional programming. Functional programming is a prime example of the declarative approach, but they are not the same thing. I realized I needed to draw the line between declarative vs imperative and functional vs object oriented. Of course, React exhibits a lot of functional programming characteristics (things to do with the virtual DOM and passing functions) which then starts to get really complicated (a good follow up blog). For now, I hope this clarified to why React exhibits a declarative approach as it did for me.

2. Declarative vs Imperative Programming

Declarative programming is a programming paradigm … that expresses the logic of a computation without describing its control flow. Imperative programming is a programming paradigm that uses statements that change a program’s state.

Declarative programming is “the act of programming in languages that conform to the mental model of the developer rather than the operational model of the machine.”

Declarative Programming is programming with declarations, i.e., declarative sentences.

The declarative property is where there can exist only one possible set of statements that can express each specific modular semantic. The imperative property is the dual, where semantics are inconsistent under composition and/or can be expressed with variations of sets of statements.

Declarative languages contrast with imperative languages which specify explicit manipulation of the computer’s internal state; or procedural languages which specify an explicit sequence of steps to follow.

In computer science, declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.

I draw the line between declarative and non-declarative at whether you can trace the code as it runs. Regex is 100% declarative, as it’s untraceable while the pattern is being executed.

// Imperative Demo:

代码语言:javascript
复制
const container = document.getElementById(‘container’);
const btn = document.createElement(‘button’);btn.className = ‘btn red’;
btn.onclick = function(event) {
 if (this.classList.contains(‘red’)) {
   this.classList.remove(‘red’);
   this.classList.add(‘blue’);
 } else {
   this.classList.remove(‘blue’);
   this.classList.add(‘red’);
 }
};container.appendChild(btn);

// Declarative Demo:

代码语言:javascript
复制
class Button extends React.Component{  this.state = { color: 'red' }  handleChange = () => {
    const color = this.state.color === 'red' ? 'blue' : 'red';
    this.setState({ color });
  }  render() {
    return (<div>
      <button 
         className=`btn ${this.state.color}`
         onClick={this.handleChange}>
      </button>
    </div>);
  }
}

3. React Hooks are a More Accurate Implementation of the React Mental Model

React Functional Components using Hooks are a More Accurate Implementation of the React Mental Model for State and Effects, than React Classes

TL;DR React made updating the DOM declarative. Hooks made components themselves declarative.

The key of React was allowing declarative code to be mapped to an imperative DOM.

This was especially true of functional components, which would simply map data to an object describing the UI. React would take this object and surgically (imperatively) update the DOM.

However, with class components, while the render function was still declarative, the class instance itself (where the state lived) is mutable - which made it harder to reason about.

The implementation for state and side-effects were within these class components - tied to the mutating instance.

React hooks are a re-conception and re-implemenation of state and side-effects in React - an implementation instead of in class components, is in functional components. As a basic definition they are functions that let you "hook into" React state and lifecycle features. But the key is their implementation with functional components in a declarative api.

"But why is this a 'more accurate implementation of the react mental model'?"

React hooks allow components to be truly declarative even if they contain state and side-effects.

State is now retrieved declaratively without mutating the structure of the component (ie as the class instance would be).

Side-effects are now declaratively aligned with state, instead of with the component's mutation.

Just as the first key of react was a declarative mapper to the DOM, hooks are the second key: providing a declarative api in the component for state and side effects.

"Um, OK, sure.. How about some code?"

Lets look at two versions of doing the same thing. The first version uses the initial class-based implementation of state and effects, and second uses the new hook-based implementation.

The example is an (very contrived) User component. An input will search for the user and display their name, which can be edited and saved.

3.1. Using React's initial class-based implementation of state and effects

代码语言:javascript
复制
/*
 * A code sample to show how React class components are
 * not the best implementation of the react mental model.
 *
 * Limitations:
 * - 1. With react classes, `this` is mutable and harder
 *      to reason about
 * - 2. With react classes, the lifecyle hooks are aligned
 *      with the component instead of the data.
 *
 * To see 1: save a user's name, and then immediately
 * change it again. You'll see the confirmation alert has
 * the wrong name (the new one, not the one which was saved).
 * Because "this" is mutated before the save finishes,
 * the wrong data is surfaced to the user.
 *
 * To see 2: Notice how the code for componentDidUpdate
 * and componentDidMount is doing the same thing? What we
 * care about is changes to "username" data but instead
 * the model here is built around changes to the component.
 */

import React from "react";

class User extends React.Component {
  state = {
    username: this.props.username
  };

  handleUsernameChange = e => {
    this.setState({ username: e.target.value });
  };

  handleNameChange = e => {
    const name = e.target.value;
    this.setState(state => ({
      ...state,
      user: {
        ...state.user,
        name
      }
    }));
  };

  save = () => {
    // Pretend save that takes two seconds
    setTimeout(
      () => alert(`User's name has been saved to "${this.state.user.name}`),
      2000
    );
  };

  async fetchUser() {
    const response = await fetch(
      `https://api.github.com/users/${this.state.username}`
    );
    if (!response.ok) {
      return {};
    }
    return await response.json();
  }

  async componentDidMount() {
    if (this.props.username) {
      if (this.state.username) {
        const user = await this.fetchUser();
        this.setState({ user });
      }
    }
  }

  async componentDidUpdate(prevProps, prevState) {
    if (this.state.username !== prevState.username) {
      if (this.state.username) {
        const user = await this.fetchUser();
        this.setState({ user });
      }
    }
  }

  componentWillUnmount() {
    // clean up any lingering promises
  }

  render() {
    return (
      <>
        Search
        <input
          value={this.state.username || ""}
          placeholder="Github Username"
          onChange={this.handleUsernameChange}
        />
        <hr />
        {this.state.user && (
          <>
            <h2>Name</h2>
            <input
              value={this.state.user.name}
              onChange={this.handleNameChange}
            />
            <button onClick={this.save}>Save</button>
          </>
        )}
      </>
    );
  }
}

export default User;

Here is the live code running. You can see point 1 described in the code comment above: save a user's name, and then immediately change it again. You'll see the confirmation alert has the wrong name (the new one, not the one which was saved).

3.2. Using React's new hook-based implementation of state and effects

代码语言:javascript
复制
/*
 * A code sample to show how React functional components useing "hooks" are a
 * better implementation of the react mental model.
 */
import React, { useState, useEffect } from "react";

const fetchUser = async username => {
  if (!username) return await {};
  const response = await fetch(`https://api.github.com/users/${username}`);
  if (!response.ok) return {};
  return await response.json();
};

const saveUser = user => {
  // Pretend save that takes two seconds
  setTimeout(() => alert(`User's name has been saved to "${user.name}`), 2000);
};

export default ({ username: initialUsername = "" }) => {
  const [user, setUser] = useState({});
  const [username, setUsername] = useState(initialUsername);

  useEffect(() => {
    const doFetchAndSet = async () => {
      const u = await fetchUser(username);
      setUser(u);
    };
    doFetchAndSet();
  }, [username]);

  return (
    <>
      Search
      <input
        value={username || ""}
        placeholder="Github Username"
        onChange={e => setUsername(e.target.value)}
      />
      <hr />
      {user.name && (
        <>
          <h2>Name</h2>
          <input
            value={user.name}
            onChange={e => setUser({ ...user, name: e.target.value })}
          />
          <button onClick={() => saveUser(user)}>Save</button>
        </>
      )}
    </>
  );
};

Again, here is this live code running. If you try to reproduce the bug from the first example, you won't be able to.

4. StackOverflow:Declarative vs. Imperative

A programming paradigm is a fundamental style of computer programming. There are four main paradigms: imperative, declarative, functional (which is considered a subset of the declarative paradigm) and object-oriented.

Declarative programming : is a programming paradigm that expresses the logic of a computation(What do) without describing its control flow(How do). Some well-known examples of declarative domain specific languages (DSLs) include CSS, regular expressions, and a subset of SQL (SELECT queries, for example) Many markup languages such as HTML, MXML, XAML, XSLT... are often declarative. The declarative programming try to blur the distinction between a program as a set of instructions and a program as an assertion about the desired answer.

Imperative programming:is a programming paradigm that describes computation in terms of statements that change a program state. The declarative programs can be dually viewed as programming commands or mathematical assertions.

Functional programming : is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state. In a pure functional language, such as Haskell, all functions are without side effects, and state changes are only represented as functions that transform the state.

The following example of imperative programming in MSDN, loops through the numbers 1 through 10, and finds the even numbers.

代码语言:javascript
复制
var numbersOneThroughTen = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//With imperative programming, we'd step through this, and decide what we want:
var evenNumbers = new List<int>();
foreach (var number in numbersOneThroughTen)
{    if (number % 2 == 0)
    {
        evenNumbers.Add(number);
    }
}
//The following code uses declarative programming to accomplish the same thing.
// Here, we're saying "Give us everything where it's even"
var evenNumbers = numbersOneThroughTen.Select(number => number % 2 == 0);

Both examples yield the same result, and one is neither better nor worse than the other. The first example requires more code, but the code is testable, and the imperative approach gives you full control over the implementation details. In the second example, the code is arguably more readable; however, LINQ does not give you control over what happens behind the scenes. You must trust that LINQ will provide the requested result.

5. Declarative Programming & React

Declarative programming is something popularized by React in the JavaScript community. It is not a new thing but just got popularized in recent days.

According to Wikipedia:

declarative programming is a programming paradigm — a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow.

This definition is nice one only if you know declarative programming but don't worry we will make sense out of it.

5.1. Declarative Programming

Declarative programming is like describing the picture, where imperative programming is the instruction for painting that picture. Declarative programming makes code:

  • more readable: Program that are easier to read because it hides the lower level detail. In declarative programming we don't even know about the lower level details of the system.
  • easier to reason about: the code we write is easier to reason about because it is much abstracted and we describe the solution instead of procedure.

As I said, declarative programming is not a new thing so there are many languages that are widely used in the industry that are declarative. Let me tell you a few of them

SQL:

SQL is a domain-specific language used in programming and designed for managing data held in a relational database. If you're reading this I don't think I need to give you any introduction of SQL it is just the de-facto standard for managing relational databases.

Look at this simple SQL query:

代码语言:javascript
复制
SELECT * FROM Employees

Here we are defining we need every Employees detail not how to get the Employees. we aren't caring about any complex data structure the database is using to store the data.

CSS:

CSS is a nice example of declarative programming, In CSS we are actually defining how the element should look like and the browser takes care of implementing that for you. You can just say this div should be blue in color and text should look bright yellow and the browser will do it for you.

Imagine if you set body to

代码语言:javascript
复制
body {
  width: 500px;
  height: 500px;
  background: palevioletred;
  color: white;
}

now the browser makes the body according to your CSS. This is the concept of declarative programming, you define the structure and the compiler/host does it for you.

5.2. Declarative programming in React

In react, You make interactive UIs by changing the state of the component and React takes care of updating the DOM according to it.

take this react code as an example:

代码语言:javascript
复制
import React from "react";

class App extends React.Component {
  state = {
    message: "Hello react"
  }
  render() {
    return (
    <div className="App">
      <h1>{this.state.message}</h1>
      <button 
        onClick={e => this.setState({message: "Hello World"})}>
          Change Message</button>
    </div>
  );
  }
}

this creates a "hello React" message along with a button, which can you see here like

When you click the button it changes the message to "Hello World".

In react the DOM is declarative. This means we never interact with DOM, the UI is updated when we change the state. This makes it easier to design UI and debug them, You can just change the program's state and see how the UI will look at that particular time.

参考:

Declarative vs Imperative: https://medium.com/@myung.kim287/declarative-vs-imperative-251ce99c6c44 Declarative vs Imperative Programming: https://codeburst.io/declarative-vs-imperative-programming-a8a7c93d9ad2 Imperative vs Declarative Programming: https://ui.dev/imperative-vs-declarative-programming/ React Hooks are a More Accurate Implementation of the React Mental Model: https://dev.to/craigmichaelmartin/react-hooks-are-a-more-accurate-implementation-of-the-react-mental-model-1k49 stackoverflow: https://stackoverflow.com/questions/1784664/what-is-the-difference-between-declarative-and-imperative-paradigm-in-programmin Declarative programming: https://subscription.packtpub.com/book/web_development/9781789530179/1/ch01lvl1sec10/declarative-programming Declarative Programming & React: https://dev.to/itsjzt/declarative-programming--react-3bh2


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档