npm i valtio
giúp việc quản lý trạng thái proxy trở nên đơn giản
Bao bọc đối tượng trạng thái của bạn
Valtio biến đối tượng bạn truyền vào thành một proxy tự nhận thức.
import { proxy, useSnapshot } from 'valtio'
const state = proxy({ count: 0, text: 'hello' })
Thay đổi từ bất kỳ đâu
Bạn có thể thay đổi nó theo cách bạn thay đổi một đối tượng js bình thường.
setInterval(() => {
++state.count
}, 1000)
Sử dụng với React thông qua useSnapshot
Tạo một bản xem trạng thái cục bộ để theo dõi thay đổi. Nguyên tắc: đọc từ các bản xem trong hàm render, nếu không, hãy sử dụng nguồn gốc. Thành phần chỉ sẽ được render lại khi phần của trạng thái bạn truy cập đã thay đổi, nó được tối ưu hóa cho việc render.
// This will re-render on `state.count` change but not on `state.text` change
function Counter() {
const snap = useSnapshot(state)
return (
<div>
{snap.count}
<button onClick={() => ++state.count}>+1</button>
</div>
)
}
Lưu ý cho người dùng TypeScript: Kiểu trả về của useSnapshot có thể quá nghiêm ngặt.
Biến số snap
được trả về bởi useSnapshot
là một đối tượng (sâu) chỉ đọc. Kiểu của nó có thuộc tính readonly
, có thể quá nghiêm ngặt đối với một số trường hợp sử dụng.
Để giảm thiểu khó khăn về kiểu, bạn có thể muốn làm lỏng định nghĩa kiểu:
declare module 'valtio' {
function useSnapshot<T extends object>(p: T): T
}
Xem #327 để biết thêm thông tin.
Lưu ý: useSnapshot trả về một proxy mới để tối ưu hóa việc render.
Bên trong, useSnapshot
gọi snapshot
trong valtio/vanilla và bọc đối tượng snapshot bằng một proxy khác để phát hiện truy cập thuộc tính. Tính năng này dựa trên proxy-compare.
Có hai loại proxy được sử dụng cho các mục đích khác nhau:
proxy()
từvaltio/vanilla
dùng để theo dõi sự thay đổi hoặc theo dõi việc ghi.createProxy()
từproxy-compare
dùng để theo dõi việc sử dụng hoặc theo dõi đọc.
Sử dụng this
dành cho người dùng chuyên nghiệp.
Valtio cố gắng xử lý hành vi this
tốt nhất nhưng nó khó hiểu nếu không quen thuộc.
const state = proxy({
count: 0,
inc() {
++this.count
},
})
state.inc() // `this` points to `state` and it works fine
const snap = useSnapshot(state)
snap.inc() // `this` points to `snap` and it doesn't work because snapshot is frozen
Để tránh rơi vào lỗ hổng này, mẫu được đề xuất là không sử dụng this
và ưu tiên sử dụng hàm mũi tên.
const state = proxy({
count: 0,
inc: () => {
++state.count
},
})
Nếu bạn mới làm quen với điều này, nên sử dụng eslint-plugin-valtio.
Đăng ký từ bất kỳ đâu
Bạn có thể truy cập trạng thái bên ngoài các thành phần của bạn và đăng ký theo dõi sự thay đổi.
import { subscribe } from 'valtio'
// Subscribe to all state changes
const unsubscribe = subscribe(state, () =>
console.log('state has changed to', state)
)
// Unsubscribe by calling the result
unsubscribe()
Bạn cũng có thể đăng ký theo dõi một phần của trạng thái.
const state = proxy({ obj: { foo: 'bar' }, arr: ['hello'] })
subscribe(state.obj, () => console.log('state.obj has changed to', state.obj))
state.obj.foo = 'baz'
subscribe(state.arr, () => console.log('state.arr has changed to', state.arr))
state.arr.push('world')
Để đăng ký theo dõi một giá trị nguyên thủy của trạng thái, xem xét subscribeKey
trong utils.
import { subscribeKey } from 'valtio/utils'
const state = proxy({ count: 0, text: 'hello' })
subscribeKey(state, 'count', (v) =>
console.log('state.count has changed to', v)
)
Có một tiện ích khác là watch
có thể thuận tiện trong một số trường hợp.
import { watch } from 'valtio/utils'
const state = proxy({ count: 0 })
const stop = watch((get) => {
console.log('state has changed to', get(state)) // auto-subscribe on use
})
Tạm dừng các thành phần của bạn
Valtio hỗ trợ React-suspense và sẽ ném ra các promise mà bạn truy cập trong hàm render của các thành phần. Điều này loại bỏ tất cả các tương tác không đồng bộ, bạn có thể truy cập dữ liệu của bạn trực tiếp trong khi phần cha chịu trách nhiệm cho trạng thái dự phòng và xử lý lỗi.
const state = proxy({ post: fetch(url).then((res) => res.json()) })
function Post() {
const snap = useSnapshot(state)
return <div>{snap.post.title}</div>
}
function App() {
return (
<Suspense fallback={<span>waiting...</span>}>
<Post />
</Suspense>
)
}
Giữ các đối tượng trong trạng thái mà không theo dõi chúng
Điều này có thể hữu ích nếu bạn có các đối tượng lồng nhau lớn với các phương thức truy cập mà bạn không muốn proxy. ref
cho phép bạn giữ các đối tượng này bên trong mô hình trạng thái.
Xem #61 và #178 để biết thêm thông tin.
import { proxy, ref } from 'valtio'
const state = proxy({
count: 0,
dom: ref(document.body),
})
Cập nhật tạm thời (cho các thay đổi trạng thái xảy ra thường xuyên)
Bạn có thể đọc trạng thái trong một thành phần mà không gây ra việc render lại.
function Foo() {
const { count, text } = state
// ...
Hoặc, bạn có thể có sự kiểm soát hơn bằng cách đăng ký theo dõi trong useEffect.
function Foo() {
const total = useRef(0)
useEffect(() => subscribe(state.arr, () => {
total.current = state.arr.reduce((p, c) => p + c)
}), [])
// ...
Cập nhật đồng bộ
Theo mặc định, các thay đổi trạng thái được gom nhóm trước khi kích hoạt việc render lại. Đôi khi, chúng ta muốn tắt gom nhóm. Một trường hợp sử dụng biết đến của điều này là <input>
#270.
function TextBox() {
const snap = useSnapshot(state, { sync: true })
return (
<input value={snap.text} onChange={(e) => (state.text = e.target.value)} />
)
}
Công cụ phát triển
Bạn có thể sử dụng Redux DevTools Extension cho các đối tượng và mảng thông thường.
import { devtools } from 'valtio/utils'
const state = proxy({ count: 0, text: 'hello' })
const unsub = devtools(state, { name: 'state name', enabled: true })
Manipulating state with Redux DevTools
Sử dụng nó với vanilla JavaScript
Valtio không ràng buộc với React, bạn có thể sử dụng nó trong vanilla JavaScript.
import { proxy, subscribe, snapshot } from 'valtio/vanilla'
// import { ... } from 'valtio/vanilla/utils'
const state = proxy({ count: 0, text: 'hello' })
subscribe(state, () => {
console.log('state is mutated')
const obj = snapshot(state) // A snapshot is an immutable object
})
Sử dụng util useProxy
Trong khi việc tách biệt trạng thái proxy và bản xem của nó quan trọng, nó gây nhầm lẫn cho người mới học. Chúng tôi có một util tiện lợi để cải thiện trải nghiệm phát triển. useProxy
trả về trạng thái proxy nông và bản xem của nó, có nghĩa bạn chỉ có thể thay đổi ở mức gốc.
import { useProxy } from 'valtio/utils'
const state = proxy({ count: 1 })
const Component = () => {
// useProxy returns a special proxy that can be used both in render and callbacks
// The special proxy has to be used directly in a function scope. You can't destructure it outside the scope.
const $state = useProxy(state)
return (
<div>
{$state.count}
<button onClick={() => ++$state.count}>+1</button>
</div>
)
}
Sử dụng util derive
Bạn có thể đăng ký theo dõi một số trạng thái proxy và tạo ra một trạng thái proxy dẫn xuất.
import { derive } from 'valtio/utils'
// create a base proxy
const state = proxy({
count: 1,
})
// create a derived proxy
const derived = derive({
doubled: (get) => get(state).count * 2,
})
// alternatively, attach derived properties to an existing proxy
derive(
{
tripled: (get) => get(state).count * 3,
},
{
proxy: state,
}
)
Sử dụng util proxyWithHistory
Đây là một hàm tiện ích để tạo ra một trạng thái proxy với lịch sử bản xem.
import { proxyWithHistory } from 'valtio/utils'
const state = proxyWithHistory({ count: 0 })
console.log(state.value) // ---> { count: 0 }
state.value.count += 1
console.log(state.value) // ---> { count: 1 }
state.undo()
console.log(state.value) // ---> { count: 0 }
state.redo()
console.log(state.value) // ---> { count: 1 }
Sử dụng util proxySet
Đây là để tạo ra một trạng thái proxy mô phỏng hành vi Set nguyên bản. API là giống với API của Set.
import { proxySet } from 'valtio/utils'
const state = proxySet([1, 2, 3])
//can be used inside a proxy as well
//const state = proxy({
// count: 1,
// set: proxySet()
//})
state.add(4)
state.delete(1)
state.forEach((v) => console.log(v)) // 2,3,4
Sử dụng util proxyMap
Đây là để tạo ra một trạng thái proxy mô phỏng hành vi Map nguyên bản. API là giống với API của Map.
import { proxyMap } from 'valtio/utils'
const state = proxyMap([
['key', 'value'],
['key2', 'value2'],
])
state.set('key', 'value')
state.delete('key')
state.get('key') // ---> value
state.forEach((value, key) => console.log(key, value)) // ---> "key", "value", "key2", "value2"
Tương thích
Valtio hoạt động với React với sự hỗ trợ của hooks (>=16.8). Nó chỉ phụ thuộc vào react
và hoạt động với bất kỳ trình điều khiển nào như react-dom
, react-native
, react-three-fiber
, và vân vân.
Valtio hoạt động trên Node.js, Next.js và các frameworks khác.
Valtio cũng hoạt động mà không cần React. Xem vanilla.
Các tiện ích mở rộng
Các hướng dẫn
Valtio không có quan điểm cố định về các quy tắc tốt nhất. Cộng đồng đang làm việc trên các hướng dẫn trên trang wiki.
- How to organize actions
- How to persist states
- How to use with context
- How to split and compose states
Chi tiết tải về:
Tác giả: pmndrs
Nguồn: https://github.com/pmndrs/valtio
Giấy phép: MIT license