Giter Site home page Giter Site logo

vue-hooks's Introduction

!!! ARCHIVED !!!

Vue now has its own hooks inspired API for composing logic: https://composition-api.vuejs.org/

This PoC has completed its purpose and will no longer be updated.


vue-hooks

POC for using React Hooks in Vue. Totally experimental, don't use this in production.

Live Demo

React-style Hooks

import Vue from "vue"
import { withHooks, useState, useEffect } from "vue-hooks"

// a custom hook...
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)
  const handleResize = () => {
    setWidth(window.innerWidth)
  };
  useEffect(() => {
    window.addEventListener("resize", handleResize)
    return () => {
      window.removeEventListener("resize", handleResize)
    }
  }, [])
  return width
}

const Foo = withHooks(h => {
  // state
  const [count, setCount] = useState(0)

  // effect
  useEffect(() => {
    document.title = "count is " + count
  })

  // custom hook
  const width = useWindowWidth()

  return h("div", [
    h("span", `count is: ${count}`),
    h(
      "button",
      {
        on: {
          click: () => setCount(count + 1)
        }
      },
      "+"
    ),
    h("div", `window width is: ${width}`)
  ])
})

// just so you know this is Vue...
new Vue({
  el: "#app",
  render(h) {
    return h("div", [h(Foo), h(Foo)])
  }
})

Vue-style Hooks

API that maps Vue's existing API.

import {
  withHooks,
  useData,
  useComputed,
  useWatch,
  useMounted,
  useUpdated,
  useDestroyed
} from "vue-hooks"

const Foo = withHooks(h => {
  const data = useData({
    count: 0
  })

  const double = useComputed(() => data.count * 2)

  useWatch(() => data.count, (val, prevVal) => {
    console.log(`count is: ${val}`)
  })

  useMounted(() => {
    console.log('mounted!')
  })
  useUpdated(() => {
    console.log('updated!')
  })
  useDestroyed(() => {
    console.log('destroyed!')
  })

  return h('div', [
    h('div', `count is ${data.count}`),
    h('div', `double count is ${double}`),
    h('button', { on: { click: () => {
      // still got that direct mutation!
      data.count++
    }}}, 'count++')
  ])
})

Usage in Normal Vue Components

import { hooks, useData, useComputed } from 'vue-hooks'

Vue.use(hooks)

new Vue({
  template: `
    <div @click="data.count++">
      {{ data.count }} {{ double }}
    </div>
  `,
  hooks() {
    const data = useData({
      count: 0
    })

    const double = useComputed(() => data.count * 2)

    return {
      data,
      double
    }
  }
})

vue-hooks's People

Contributors

dillonchanis avatar isnifer avatar tjfogarty avatar yyx990803 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-hooks's Issues

Usage with functional components?

I was excited by React Hooks because it provides state into functional components and I thought this is the same. But reading source code I understood that this implementation for stateful components. Why do I need use hooks instead of the local state? Maybe I missed something in examples, but I didn't found something that I couldn't create without hooks.

Add Usage in functional Vue Component.

According to #14 , I thought we can add some use case of vue-hooks in function component (maybe jsx).

Below is a very simple demo, could I send a PR to add this?

import { withHooks, useState } from 'vue-hooks';

const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>
        Current value is <strong>{count}</strong>
      </p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
    </div>
  );
};
export default withHooks(h => h(Counter));

Update documentation and examples to use `function` instead of `lambda`

I know it is a bit more verbose, but the lambdas do not allow the hook to access the component using this. You cannot capture the instance in a closure outside of the the lambda either, because the component has not been instantiated when the hooks are loaded.

const Foo = withHooks(h => {
  // `this` is not defined so the lambda does not capture it. 
  const double = useComputed(()=>return data.count * 2 )
}
const Foo = withHooks(h => {
  // The computed function will be called with `this` set to the component because lambda is not used.
  const double = useComputed( function() { return data.count * 2 } )
}

@yyx990803 I have to say this is similar to another issue I see this all the time:

data:()=>({foo:'bar'})

When IMHO is should always be:

data(){ return {foo:'bar'} }

The reason I say always is that you do not have access to props, $route, $store, etc if you use the lambda notation, so it really is a best practice to have your Vue code always ready to access this as a valid component instance.

I have submitted a pull request updating the documentation and examples.

Typescript support

In the future, do you intend to support typescript with typings for this library?

useComputed 似乎和 withHooks 并非强相关?

useComputed 中会先判断 ensureCurrentInstance,而后者的提示是 “can only be called in a function passed to withHooks”;但核心逻辑只是判断 currentInstance,用 hooks mixin 的方法也能确保,提示似乎不妥

[feature]Maybe we can provide createElement in hooks, to cache the created vdom? Or just passing instance into hooks?

Because the hooks are run in beforeMount where the instance object is already created.
We know that createElement is bound to the instance, so may I assume that meanwhile we can createElement and cache the result?

The API might looks like

hooks(props, instance) {
    const h = instance.$createElement;
    const cachedVNode  = useMemo(....); // some custom hook
    return {
        cachedVNode,
    };
},

why we can't use `this` in `hooks` property methods?

new Vue({
    hooks() {
        // we can't use `this` here
        console.log(this); // undefined

        useEffect(function() {
            // but we can use `this` here when we use `function` keyword
            console.log(this); // vm;
        }, []);
    },
});

see above, we can use this in many other property methods like data, mounted, updated and so on but hooks, why do we design it like this?

Computed depending on useComputed won't update on change

First of all this experiment looks promising and it is interesting as well how will the hooks play well with normal vue components + functional ones. I've found the following problem when experimented with it.

I'm using the regular vue component version with template and the hooks property. If I define a computed via useComputed and then I build computed on the cm based on the hook computed that computed won't be updated when state changes in the hook state. Without hooks in regular vue components the same thing works if one of the computed depends on anoter and if that changes it will cause the reeavulation of the other one as well. With hook computed it doesn't work yet.

Checking the vue source how computeds created on the vm I see what is missing. There the getter of the computed is wrapped in another function which uses the watcher of the computed (all computeds has their own watcher vs hook computed has no watcher yet) to evaulaute and more importantly to call depend to register the dependency when it is used. This will probably needed in the hook version too somehow.

A simple example to reproduce the problem:

export default {
  template: `
    <div>
      count: {{count}}
      double: {{double}}
      quadruple: {{quadruple}}
      <button type="button" @click="increment">inc</button>
      <button type="button" @click="decrement">dec</button>
    </div>
  `,

  hooks() {
    const data = useData({ count: 0 })
    const double = useComputed(() => data.count * 2 )
    const increment = () => data.count = data.count + 1
    const decrement = () => data.count = data.count - 1
    return {
      count: data.count,
      double,
      increment,
      decrement
    }
  },

  computed: {
    quadruple () {
      // this won't update on increment/decrement
      return this.double * 2
    }
  }
}

Access context in component constructed using `withHooks`

If we do something like:

<template>
  <Foo some-prop="someProp" />
</template>

<script>

const Foo = withHooks(h => {
  // state
  const [count, setCount] = useState(0)

  return h("div", [
      h("span", `count is: ${count}`),
    ]
})

export default {
  components: { Foo }
}
</script>

There should be some way to access the props passed too <Foo />. I can't find a way at the moment. I tried to modify the source but couldn't figure it out. Is there some way to pass context as the second arg to withHooks? Eg:

const Foo = withHooks((h, ctx) => {
  // ...
])

I understand this is just a demo, but it's still exciting to explore new possibilities.

Can't access hook "data" in created lifecycle

During my experiments with hooks I found that the hooks "data" cannot be accessed in the created lifecycle method. In mounted they will be accessible. Lets say we want to replace mixins with hooks then maybe this needs to be fixed as well, because we might do sy on created with the shared stateful logic provided by the hooks.

vue3.0有没可能更多借鉴clojure?

前段时间发了个状态,如下,尤大大考虑下,希望一如既往,vue3.0不是技术最牛的,但仍是用户体验最好的:

一直用vue,喜欢它的简洁。但不喜欢拆分组件,因为麻烦(Facebook有数千个研发,所以拆了1万个组件,咱没这么多人),往往一个页面就是一个组件,但是,当页面过于复杂时就有些不好维护了,目前手头的项目单页面过千行的已经不少了,这样模板500行,逻辑500行,可维护性已成问题。拆分又不想,否则用react去了。为了解决可维护性问题,前段时间一口气研究了所有的函数式编程语言,感觉最有潜力的的是Haskell/PureScript和Clojure(script),尤其是Clojurescript,结合reagent dom框架,感觉可维护性会有质的提升,而且真心喜欢这门语言的风格。可惜Clojure(script)的生态还不是太好,路由,服务端渲染都没有特别完美的框架,也担心引入新语言会影响产品进度。为了不折腾,新的项目还是继续vue。前两天,react-hook出来,以普通函数出现的UI组件简直和Clojure(script)的一模一样,但后者一切都是函数更有过之而不及。听说vue3.0明年出来,真心希望在这块超越react,学习Clojure(script)好的东西,做出更受**人喜欢的新版来。

我不是什么FP党,但使用Clojure(script),我感觉是可以非常自由,简单地纯函数式组织代码,并且有很好的可维护性:

(ns pages.home
  (:require [reagent.core :refer [atom]]
            ["antd" :as A]
            [ajax.core :refer [GET POST]]
            [cljs.reader :refer [read-string]]))


; state
; ----------------------------------------------
(defonce users (atom []))


; action
; ----------------------------------------------
(defn get-users []
  (GET "http://localhost:1110/user"
    {:handler #(reset! users %)}))


; ui
; ----------------------------------------------
(defn ui-header []
  [:div
    [:h3 "This is home page"]
    [:> A/DatePicker]
    [:> A/Alert
      {:banner true
       :closable true
       :message "请核对手机号"
       :onClose #(js/alert "你关了我!")}]])


(defn ui-counter []
  (let [counts (atom 0)
        increment (fn [e] (.preventDefault e)(swap! counts inc))
        decrement (fn [e] (.preventDefault e)(swap! counts dec))]
    (fn []
      [:div
        (case @users
          []  [:div "加载中..."]
          nil [:div "暂无内容"]
              [:div (str @users)])
        [:button.btn {:on-click #(decrement %)} "-"]
        [:button     {:disabled true} @counts]
        [:button.btn {:on-click #(increment %)} "+"]])))


; init
; ----------------------------------------------
(defn init []
  (js/setTimeout get-users 100)
  ; 本地存储
  (js/localStorage.clear)
  (js/localStorage.setItem "prefs" "{:a localValue1 :b 2}")
  ; 全局常量
  (js/alert goog.global.WX-URL))


; main
; ----------------------------------------------
(defn main []
  (init)
  [:div
   [ui-header]
   [ui-counter]
   [:div.mt2
     [:a {:href "/about?id=1"} "go to the about page 1"]]
   [:div.mt2
     [:a {:href "/about?id=8"} "go to the about page 8"]]])

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.