GithubHelp home page GithubHelp logo

frejs / fre Goto Github PK

View Code? Open in Web Editor NEW
3.7K 39.0 351.0 6.37 MB

:ghost: Tiny Concurrent UI library with Fiber.

Home Page: https://fre.deno.dev

License: MIT License

JavaScript 2.29% TypeScript 97.71%
fre jsx hooks fiber react-like reconcilation keyed-reconcilation-algorithm vdom vdom-library reconciliation-algorithm

fre's People

Contributors

1123612483 avatar a145789 avatar ahaoboy avatar can-chen avatar chenjigeng avatar dependabot[bot] avatar duhongjun avatar eugene-daragan-pandadoc avatar fossabot avatar hyrious avatar joylei avatar juojuo avatar kolodziejczak-sz avatar lishaoxin123 avatar liulinboyi avatar lvbaiying avatar malash avatar microj avatar mindplay-dk avatar prettykernel avatar timetravelcyn avatar winesu avatar wu-yu-xuan avatar xyuu avatar yiliang114 avatar yingpengsha avatar yisar avatar zheksoon avatar zhmushan avatar zhongmeizhi 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

fre's Issues

What is the options object for?

The reconciler module exports an options object - can you explain what these options are for?

  1. There's a few check for options.end in the reconciler itself, but I don't see any code (in either src or demo) that actually sets options.end?

  2. There's an options.commitWork callback, which seems to completely override the built-in commitWork() function in the reconciler - it's being set only in one of the demos, where it just does a console.log().

  3. There's an options.platform value being set in the same same, which doesn't appear to be used anywhere.

What was the idea with (these) options?

Maybe it's left over from early development or something?

Errors get silenced?

If an error occurs, for example, while executing a hook, it looks like the error gets silenced? You don't see an error on the console, or anywhere.

I think I've run into this a number of times while writing tests - many times I've had jest hanging for several minutes before finally reporting a timeout, with no real clue why this was happening... An actual exception getting throw would cause jest to halt - but instead, you have to wait for every test to time out, which can take a long time.

I'm also now running into this in my Component example - I had forgotten to declare App.componentDidMount and the effect in useComponent was attempting to call this... Instead of an error on the console, all the buttons simply stopped worked - there was no way to see why.

I've been suspecting this had something to do with flushWork, so I made a quick change:

function flushWork(iniTime) {
  try {
    return workLoop(iniTime)
  } catch(e) {
    console.error(e)
  } finally {
    currentTask = null
  }
}

This revealed the error on the console! So far so good.

But this is just printing an error-message now, so this still wouldn't fix the problem under jest, since console.error just prints an error-message but doesn't work like an exception - so jest still won't know it happened.

I also tried something like this:

function flushWork(iniTime) {
  try {
    return workLoop(iniTime)
  } catch(e) {
    throw e // <-- re-throwing the error
  } finally {
    currentTask = null
  }
}

But that also just silences the error. 🤨

It's weird, because try/finally without a catch isn't suppose to handle errors at all - so if you do this:

function flurp() {
  throw "UGH";
}

try {
  flurp()
} finally {
  console.log("NP!")
}

You will see the error-message for the exception, as expected.

I can't figure out why it behaves differently in fre... 🤷‍♂️

(I also suspect this could be related to the problem with the browser stalling, as mentioned on Twitter - if it keeps running through the work-loop trying to complete the same task, but an error prevents it from ever finishing the next task in the queue? Just a guess.)

Any ideas?

new Context, Suspense and so on...

Because the new version adds priority scheduling, it is impossible to reduce the size to 1 kb.
There is some space to support Context, Suspense, and other features.

This issue will be used to discuss them.
Whether it is needed and how to implement?

Property-names get lower-cased?

It looks like property-names are getting lower-cased?

For example, <div className="red"> gets rendered as <div classname="red">, which doesn't work, since the property-names are case-sensitive:

image

I've been looking through the code, and can't figure out where or how this happens.

If I take the same example and import h and render from e.g. preact, it works fine, so I don't think it's CodeSandbox mangling the code or something? 🤔

I only see one call to toLowerCase() in the code, and it applies only to event-handlers.

Any ideas?

Missing clean-up of refs

It looks like neither callback refs nor object refs are getting cleaned up.

In the case of callback refs, this means you can't use a callback to clean up any side-effects - here's an example:

const DatePicker = () => {
  const init = el => {
    if (el) {
      // initialize date-picker using some third-party plain JS lib...
    } else {
      // clean up any date-picker pop-ups that may still be open...
    }
  }
  return <span ref={init}/>
}

The clean-up call never happens, so we currently can't really rely on refs for this sort of thing.

Note that this may not be a very useful feature in a codebase that exclusively uses hooks, but if we want to support class-based components at some point down the line, many class-based libraries rely on refs for side-effects and clean-up.

I noticed earlier that object refs also do not get cleaned up - so if you have something like this:

const Component = () => {
  const item = useRef()
  const [showItem, setShowItem] = useState(true)

  // ...

  return (
    <div>
      {showItem && <div ref={item}/>}
    </div>
  )
}

If showItem gets updated to false, item.current will still point to the DOM element, which gets removed from the DOM, but now it can't be garbage-collected.

As always, I also compared the behavior with preact, and it also deviates from react on this point, so I have opened an issue here as well.

As explained in that issue, I am not actually sure if what I reported there is a bug in preact or react, since preact appears to behave more accurately as it's described in the React docs.

Until we know if the behavior regarding null updates is correct in react or preact, I would actually advise you wait to address this issue - there is definitely an issue in fre either way, since it doesn't remove references at all, but at the moment I couldn't tell you precisely if react or preact dispatches the null updates correctly, so lets wait and see.

For comparison, here's the output from all three:

image

Note that, besides the missing null updates, it also looks like callback refs are also currently being dispatched twice during every update in fre.

It could be you actually meant for one of those calls to be the null update after clearing out the internal reference? That would seem to bring the behavior very close to that of react as it behaves currently.

You might to address that detail right away, as well as the null updates missing after removal.

Or just wait and see who's right between react and preact first. 😏

Feature: class-based Component API

Should we add support for the class-based Component API?

This could also be implemented as a separate package - and, interestingly, would work with React/Preact as well, enabling them to use hooks in render-methods too.

Here's a working prototype:

https://codesandbox.io/s/fre-component-i6sbs

For now, the prototype requires manually calling a useComponent function to wrap a Component subclass to a function using only hooks. (We might consider integrating this in the h function, where you could check if e.g. type.prototype.render is present, which it is on Component subclasses, but isn't on functional components.)

Features supported so far:

  • constructor(props)
  • componentDidMount()
  • componentWillUnmount()
  • props
  • state
  • render()
  • forceUpdate()
  • static defaultProps
  • static displayName
  • setState(state, callback)
  • setState(function, callback)
  • shouldComponentUpdate(nextProps, nextState)
  • componentDidUpdate(prevProps, prevState, snapshot)
  • static getDerivedStateFromProps(props, state)
  • static getDerivedStateFromError(error)
  • componentDidCatch(error, info)
  • getSnapshotBeforeUpdate(prevProps, prevState)

fre v2 RFC

最近实习太忙了,没有一个集中的时间去重构 fre

每天都是早上很早起床上班,到了公司开始摸鱼,摸到下班时就到群里划划水,然后回家看小说做梦

时间安排的非常紧凑,仿佛我变成了一个极度自律的人!

所以 fre 的下次更新,预计是开学后。

这次更新主要包含以下:

  1. 基于动态帧率的时间切片

如题,我已经找到了一个在 1kb 内实现时间切片的方法而不是直接使用 ric
ric 无法使用的原因是,它不仅兼容性不好(小程序不支持),而且帧率也无法手动调整,和浏览器同步容易丢帧,而且后续如果需要搞优先级也很麻烦

  1. Static 组件

这个组件的作用是,它的孩子将会直接被重用而不参与遍历
它的灵感来自于 vue3 的 block tree,但是作用机制是相反的,block tree 是通过 v-for ,将 v-for 以外的静态元素默认为不需要遍历的元素,同时尤雨溪也说 jsx 没有足够的信息找到这些元素

他当然是瞎说的,我可以提供一个组件充当信息

最终的目标都是一样的,减少静态元素的遍历

  1. 新的抽象过程

我不得不考虑如果大于 1kb 时,我需要将整个遍历过程简化,目前 fre(react)的遍历存在以下不足:

  • 每次都是从 root 往下遍历整棵树
  • effectList 的拼接比较多余

这两点,虽然 react 认为 js 遍历足够快,不需要优化,但是不得不承认,diff(减少 dom 操作)和切片(缩短单位时间),这些走投无路的优化行为都做足了,就差 js 遍历层没有优化了

所以我正在思考一个更优秀的抽象思路

  1. new Context API

很尴尬的是,现在的 fre 的 context 还是有问题的
我无法复现 react 的 context,它那个实现,我抄过来,就超过 1kb 了
但是目前发布订阅的实现也不完美
好在我想到了更合理的思路,可以简单方便快捷的实现它

以上,想起来再补充

Stalling tests: effects not always triggered?

As mentioned, I had some problems with tests stalling - it seems the test locks up and runs forever under certain conditions, I'm not sure why.

I worked around it by adding an extra <div> wrapper to the <Component/> in the tests for useEffect that you just merged.

Here's a branch with the extra <div> wrappers removed:

master...mindplay-dk:update-stalling

Can you try this branch on your local system and see if you can find the problem?

I spent a long time on this and I can't figure it out.

Most likely it is caused by a promise that never resolves - so it could have something to do with effects not being triggered; I'm using effects to in testUpdates to detect when each update completes... I'm not sure if that's why, but that's my best guess at this point.

Update fails with scrambled DOM result

I created the following small test example:

https://codesandbox.io/s/determined-butterfly-uxuic

At the top of the index.jsx file, you can swap the two import statements between fre and preact to see how this was supposed to work.

With preact:

preact

With fre:

fre

As you can see, the app breaks down as soon as you try to add another counter.

(Is key actually supported? Without support for key-based diffing, it's very hard to make any meaningful use of stateful functional components...)

About Fre's scheduler

Fre's scheduler is discussed here.

First I implemented a prototype scheduler here, but I found that react gave a better implement two days ago.

linked list vs min heap

There is a sorted queue, which we can implement using linked list or array.

React is implemented with a min heap in v16.10, which makes it standard and faster.

I think linked list is better for fre now, but heap is the best for sorted queue.

I need to reassess whether to use the min heap

Any ideas here? @mindplay-dk @yiliang114

parcel构建问题

import { h, render, useState } from 'fre'
console.log(render)
undefined

fre: 1.4.9

引入其它包测试工程没问题
是不是fre输出包格式有问题?

currentInstance 为 null

import { h, render } from './fre/src/index'

function App() {
  return (
    h('div', {}, '123')
  )
}

render(App(), document.getElementById('root'))
reconciler.js:45 Uncaught TypeError: Cannot read property 'effects' of null
    at performWork (reconciler.js:45)

大概跟了下代码,currentInstance 只在 updateHOOKComponent 里面被设定,但是代码走不到那个函数

useEffect 第二个参数传空数组,应该只执行一次,但是 fre 却不执行

function Counter() {
  const [count, setCount] = useState(0)
  useEffect(() => {
    document.title = "hello fre"
  }, [])
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

在 useEffect 第二个参数为空数组的时候,useEffie 在组件加载加载的时候只执行一次,但是fre没有执行。

Consistent export statements

Do you prefer inline export statements?

export default foo = () => { ... }

Or separate export at the end of file?

const foo = () => { ... }
const boo = () => { ... }

export { foo, boo }

Right now, it's a mix - lets pick one and be consistent?

Plan to rewrite Fre

as you know,Fre is using Proxy to make hooks come true
but,A problem is that Component-based is not complete.

I will rewrite this part of logic to make not only hooks but also components really come ture~

Thanks for your attention.

如你所见,现在的 fre 是使用 Proxy 实现的 hooks API,但是它的问题很致命,就是无法实现完整的组件化
目前来看是无解的

我最近研究了 react 的 hooks,有了新的灵感,准备重构。

我会重写这部分逻辑,通过一个最小实现来实现和 react 行为一致的 hooks API

useContext问题

代码:
let { h, render, useState, createContext, useContext } = window.fre const ctx = createContext(0) function App() { const [count, setCount] = useContext(ctx) return ( <div> <h1>{count}</h1> <button onClick={() => setCount(count + 1)}>+</button> <Other /> </div> ) } function Other() { const [count, setCount] = useContext(ctx) return <h1>{count}</h1> } render(<App />, document.getElementById('app'))

环境:
[email protected]/fre.umd.js
@babel/[email protected]
chrome 75
现象:

  1. 点击 + 按钮, other组件不刷新
  2. count 到13 , chrome卡死

@132yse

Language version?

I noticed you removed my use of the ... spread-operator and replaced it with a reference to an old-fashioned merge() function.

What version of JS are you targeting?

I personally would prefer writing the library using modern language features and APIs of "evergreen" browsers - since we already have Babel in place, we can compile down to older language versions and let Babel deal with the missing operator.

The ES-module version of the library could leave the source-code as-is, spread operators and all, since ES modules aren't supported in IE anyway.

On that note, the README probably should clarify this point.

思考 fre 重构

fre 重构汇总:

高低优先级的调度

目前 fre 的渲染流程中,render -> reconciler -> commit 全都是低优先级
这是错误的

关键是,整个调度过程中,优先级何为高,何为低?
按照正常思维,应当是前一帧的 ric 中执行 reconciler 后一帧的 raf 中执行 commit
也就是说,操作 dom 的行为应该是高优先级,链表的遍历、比对行为,为低

所以重构第一点,就要搞定这个优先级的调度

diff keyed

说实话,我一直企图忽视 diff,但是也不能一点优化措施都没有
准备加一下 keyed,但是不准备做更多的优化了

更新队列

requestIdleCallback 这个 API 真的令人绝望,我认为它内部存在微任务是最安全的
所以这次准备重构更新过程为微任务

Add callback argument to render()

I just noticed in React docs that there's a third argument callback in ReactDOM's render() function:

ReactDOM.render(element, container[, callback])

According to the docs:

If the optional callback is provided, it will be executed after the component is rendered or updated.

This would be very useful for testing, at least.

What do you think about adding support for this?

关于超级小的调度思路

  1. 动态的得到当前设备的 fps
  2. fps + requestAnimation 实现时间切片
  3. 根据不同优先级(数字常量)计算过期时间
  4. 根据过期时间安排更新

事实上,react 安排优先级是为了实现高优先级打断低优先级,suspense 这种功能的

fre 不需要,fre 只需要前两步,如果任务足够简单,不需要 16ms 就可以执行完,此时就提高帧率到它实际需要的时间,保证它执行完马上进入下一帧,相反如果任务复杂,则需要按照计算后的 fps (连续两帧超过16ms,则取帧率高的那帧的帧率)安排切片更新

这样一来,不仅可以切片,也能避免时间浪费,还可以在 1kb 内搞定

注释太少了

希望把注释加起来!想要做好的开源,注释很重要!

Updates cause all siblings to update

I've noticed another problem with reconciliation.

It looks like updates (e.g. triggered by setState via a useState hook) cause not only the affected component to update, but the entire range of sibling components after to it.

image

Only the sibling components after the affected component get updated - siblings before the affected component do not.

So, if you update counter 1, counters 2 and 3 also updates. If you update counter 2, counter 3 also updates. If you update counter 3, only counter 3 updates.

You can try it here:

https://codesandbox.io/s/fre-demo-uxuic

I will try to add a test-case to catch this issue.

Infinite loop (in scheduler?)

(You said in #84 you thought this problem was fixed, but I still have this issue with version 1.8.7 - I'm opening a separate issue to avoid getting sidetracked in other issues.)

I've created a preliminary benchmark, which sometimes causes Chrome to lock up:

image

The only way out is to kill the browser tab via Chrome's task manager.

To recreate the problem, be sure to build the fre benchmark with npm run build-prod - for some reason, the problem doesn't seem to appear with npm run build-dev, not sure (?)

Click on the button to create 10,000 rows - then click repeatedly on the "update" and "swap" buttons, which seem most likely to trigger the problem.

关于 fre 的组件化

一个很不幸的消息,就是 fre 的组件化实现,仿佛不可能

先说一下 fre 的工作流程:

render -> rerender -> patch -> state(set) -> rerender -> ···

也就是说,每次 rerender 都会重新渲染整个 vnode tree,可是比较不幸的是,当这棵树上有 function 的时候,是没办法得知新旧的的子 function 的状态的,这非常令人绝望

接下来就是要想办法解决这个事情::>_<::

当然终极目标还是能够做到 按需rerender (几乎不可能)

嗯……以上,加油√

Strange line

I'm still concerned about this line in the scheduler:

  if (!inMC) planWork() && (inMC = true)

planWork under node is actually setTimeout, which returns a truthy timer handler, so inMC = true always executes.

But in the browser, planWork is actually port.postMessage(null), which returns void, so inMC = false never executes.

I don't know how come this doesn't seem to matter one way or the other - but it definitely seems like a bug: planWork does not have a defined return-value, and that line clearly expects a boolean return-value... You must have meant for this conditional to actually do something?

Bug in useMemo (?)

I noticed a problem with useEffect() - the second argument (changed props) which gets passed on through to useMemo(), is not respected: the effect is triggered every time anything updates; even if the component itself doesn't update and another component on the page does.

As you can see in this example, the component should only update if count has changed - but the effect runs for all three components:

image

I narrowed it down to to useMemo(), where the last time triggers the change every time:

image

As you can see, it correctly detects that isChange is false - but still triggers the update, because isMounted is always undefined.

I will try to add a failing test for this problem.

Problem with useCallback() and useMemo()

I think I've spotted a problem with useMemo (and useCallback) which don't seem to use the cursor to create unique states.

export function useCallback (cb, deps) {
  return useMemo(() => cb, deps)
}

export function useMemo (cb, deps) {
  let wip = getWIP()
  if (isChanged(wip._deps, deps)) {
    wip._deps = deps
    return (wip._memo = cb())
  }
  return wip._memo
}

These write directly to a _memo field, which means you can only call one of these functions, and only once, as it will overwrite this field.

Have a look at Preact's implementation for reference.

We should add a test to show that multiple calls to these functions will work correctly. (Actually, anything that relies on side-effects needs to be tested with at least two calls to show that it works.)

Something got broken in 1.8.1 or 1.8.0

Here's a little experiment I've been working on:

https://codesandbox.io/s/fre-demo-iefch

I wanted to see if it was possible to implement React's class-based Component API using hooks.

This was working fine until release 1.8.1 - so the last release that worked for me was 1.8.0.

Try changing the dependency version from 1.8.0 to 1.8.1 in the sandbox, and all the calls to setState for some reason stops working. 😶

I suspected this had something to do with my recent change inlining the setter-function - but I've tried reverting that change, and it still doesn't work, so that's not why.

Here's a diff between 1.8.0 and 1.8.1.

Can you spot the problem in these changes?

Whatever it is, it's obviously not covered by tests yet...

Performance regression

With changes in #86, I noticed a drastic performance regression. 🤔

image

image

Could it be that it was just benchmarking substantially better before because it wasn't deallocating memory - and thereby wasn't benchmarking all the memory shuffling it actually has to do?

It doesn't seem like that can reasonably account for a 2-3 x slowdown though? (in some cases more.)

diff keyed 相关整理

有关于 keyed 的源码,可以看 preact 的实现:

https://github.com/developit/preact/blob/master/src/diff/children.js

首先,keyed 是针对于 children[] 的,每个孩子都有唯一的 key

假如,旧的 children 是 ABCD,新的是 BCDE

此时 ,我们需要做的动作,应该是两步,1. 移除A,2. 插入E

具体的比较过程是,B和A相比,发现啊不一样,然后B与B相比,发现一样,删掉A,然后C和C相比,D和D相比,发现都一样,不动。E找不到索引了,插入

假如,旧的children是ABCD,新的是DCBA,此时,应该是,D和A相比,然后和B相比,都不对,插入D。然后C和B相比,不对,C和C相比,对了,删掉B。B和C相比,和D相比,不对,插入B。A没得比了,插入A,最后删掉剩余的,应该进行六次操作

但是如果是直接替换的话,理应只需要四次操作
所以这就有问题了,不仅没有优化,还拉低性能

所以与 keyed 配套的 reused,就是我事先,先缓存好 key 和 vdom
当我发现剩余的 vdom 里有我需要插入的 vdom,我直接从缓存对象里拿

拿完即删

这样,我插完,就不用拔出了
相当于没有优化√

所以事实告诉我们,当我们操作 children 的时候,不要打乱顺序重排,最保险的行为是 push 和 pop

Key-based reordering doesn't work

I have started working on a test for key-based element reordering.

I suspected this wasn't working, so I decided to build a practical example:

https://codesandbox.io/s/fre-demo-uxuic

In this example, I have a list of counter IDs only (in counters in App) with the actual counter values being maintained in instances of Counter.

As you can see, I'm using a key-attribute on the <Counter/> instances, so the instances (with state) should be preserved between updates - which they are.

But the order of the component instance elements does not get updated.

Here's an image to explain:

image

As you can see in the console the "Remove" button handler has reversed the order of the counter IDs from 1, 2, 3 to 3, 2, 1.

But as shown with the red lines, the order of the components in the DOM is still 1, 2, 3, so the reordering doesn't seem to work.

Timing of useEffect (error in render-test)

The fix in #86 created a problem with the timing of useEffect.

You wrote:

Before I implement the async useeffect, I must use setTimeout to ensure that I get the correct result after DOM operation.

To work around this regression, you introduced a call to setTimeout, which shouldn't be necessary.

An ordinary use-case for effects is in conjunction with refs - consider the following basic example, which currently doesn't work:

const Component = () => {
  const ref = useRef();

  useEffect(() => {
    console.log(ref.current); // ❌ undefined
    // ref.current.style.backgroundColor = "red"; 💣
  }, []);

  return <div ref={ref}>HELLO</div>;
};

Note that, due to #84, I had to comment out the style assignment in the example above - this is just so you can see ref.current showing as undefined on the console, which should be the element reference.

So the effect is firing too soon - before the DOM updates have been applied.

To demonstrate precisely how this should work, I created an example using all 3 kinds of effects to generate console output that precisely demonstrates the order in which effects and clean-up should be dispatched:

https://codesandbox.io/s/smoosh-silence-1unrp

To demonstrate the timing of effects and clean-up in relation to the DOM updates, the result of the test function is printed every time - this function tests the DOM to see if the element exists and has the expected state, meaning the expected change has been applied to the DOM. In other words, this tells us whether the effect (or clean-up) function is being called BEFORE update or AFTER update, and you can see this for every call on the console.

Here is a comparison of output between react, preact and fre:

image

During creation and updates, the timing of dispatch of effects and clean-up against DOM updates is currently the opposite of React. (During removal, it is correct.)

Also, the order is wrong, with new effects being dispatched before clean-up of previous effects - as well as clean-up from effects at creation being dispatched much later during removal instead of getting cleaned up during the next update as expected.

(I hope this is helpful... The approach of hands-on "tests" like this is very fragile, but I still don't know how to turn this into an automated test - which is definitely something we need to do to prevent any future regressions...)

Again refactor for fre

  1. add scheduler
  2. refactor keyed diff for component
  3. use typescript
  4. add useRef useCallback
  5. add test

Scheduler queue issue

It looks like there's a problem with the queue.

With the benchmark, try clicking quickly on several buttons, for example:

  • "Create 10,000 rows"
  • "Update every 10th row"
  • "Update every 10th row"
  • "Update every 10th row"
  • "Swap rows"
  • "Swap rows"
  • "Swap rows"

Because the async renderer leaves time-slices open for UI interaction, users can actually click on buttons (etc.) to trigger calls to (for example) a useState-setter function, while the reconciler is still working.

Presumably, additional updates should go into the queue, and any additional updates should go to the back of the queue and run later?

My guess is, it ends up trying to "start" the work-loop twice?

(I suspect this might also be a problem for projects with more than one render-root - e.g. with more than one call to render call for independent UI areas on a single page...)

useEffect vs useLayoutEffect

Should we add support for useLayoutEffect?

From this article about the difference:

useEffect

...this runs after react renders your component and ensures that your effect callback does not block browser painting.

useLayoutEffect

This runs synchronously immediately after React has performed all DOM mutations
...
Your code runs immediately after the DOM has been updated, but
before the browser has had a chance to "paint" those changes (the user doesn't actually see the updates until after the browser has repainted).

From the React docs:

useLayoutEffect

The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

Also from the React docs:

Timing of effects

Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event.

From my understanding, if you had an implementation of useLayoutEffect, adding useEffect could be as simple as this:

function useEffect(fn, deps) {
  useLayoutEffect(() => setTimeout(fn, 0), deps)
}

I did notice Preact has two separate implementations and dispatches them differently, so I'm not sure about this.

What do you think?

fre 1.0 重构总结

  1. diff 方案,换 preact 的方案
  2. proxy 劫持,找个性能好的递归方式
  3. time slicing
  4. path updating

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.