react 有很多种状态库,redux、mobx、recoil 等等,这些无一例外都比较难以使用,
相较于这些状态库,hox.js 就比较简单,并且容易理解。
首先创建一个简单的库。
简单的命名一个文件
/src/hox/counter.hox.ts
import { createStore } from "hox";
import { useState } from "react";
/* 任意一个 custom Hook */
const [useCounterStore, CounterStoreProvider] = createStore(() => {
const [count, setCount] = useState(0);
const decrement = () => setCount(count - 1);
const increment = () => setCount(count + 1);
return {
count,
decrement,
increment,
};
});
export {
useCounterStore,
CounterStoreProvider,
};
然后在页面上使用它。
import { useCounterStore , CounterStoreProvider} from "@/hox/counter.hox"
const Child_1 = () => {
const CounterStore = useCounterStore()
return (
<div>
<p>Child_1</p>
<button onClick={() => CounterStore.increment()}>increment</button>
</div>
);
};
const Child_2 = () => {
const CounterStore = useCounterStore();
return (
<div>
<p>Child_2 ---</p>
<p>CounterStore.count:{CounterStore.count}</p>
</div>
);
};
function TestHox() {
return (
<CounterStoreProvider>
<Child_1></Child_1>
<Child_2></Child_2>
</CounterStoreProvider>
);
}
export default TestHox;
这样在页面上就有了一个可供使用的状态库了。
hox 的 createStore 它接受一个函数和一个options,返回一个数组,数组的第一位是use***Store,第二位是 ***StoreProvider的组件。
在函数中返回的 state 和 function会被存入 ,在父组件使用 ***StoreProvider 将子组件包裹,就能在子组件中使用使用 use***Store 引用状态数据和函数了。
点击按钮后,绑定的事件会去更新状态,count就增加了。
hox 还有一个全局状态 叫 createGlobalStore,这个东西创建的状态需要被 hox 的 HoxRoot 组件作为 Provider 发放。
在hox文件夹下创建一个 index.hox.ts 和一个 user.hox.ts,并且改一下 counter.hox.ts
counter.hox.ts -->
import { useState } from "react";
const CounterStore = () => {
const [count, setCount] = useState(0);
const decrement = () => setCount(count - 1);
const increment = () => setCount(count + 1);
return {
count,
decrement,
increment,
};
}
export {
CounterStore
}
index.hox.ts -->
import { createGlobalStore } from "hox";
import { CounterStore } from "./counter.hox";
import { UserStore } from "./user.hox";
const [useHoxStore, getHoxStore] = createGlobalStore(() => ({
useCounterStore: CounterStore(),
useUserStore: UserStore(),
}));
export { useHoxStore, getHoxStore };
user.hox.ts -->
import { ChangeEvent, useMemo, useState } from "react";
const UserStore = () => {
const [user, setUser] = useState({
name: "海星吧",
age: 24,
});
const userName = useMemo(() => user.name, [user.name]);
const userAge = useMemo(() => user.age, [user.age]);
function setUserName(name: string) {
setUser((user) => ({
...user,
name: name,
}));
}
function setUserAge(age: number) {
setUser((user) => ({
...user,
age: age,
}));
}
function handleInputUserAge(e: ChangeEvent<HTMLInputElement>) {
setUserAge(parseInt(e.target.value));
}
return {
user,
userName,
userAge,
setUserName,
setUserAge,
handleInputUserAge,
};
};
export { UserStore };
在app组件中使用 HoxRoot 包裹。
import { HoxRoot } from "hox";
import AppRouter from "./router";
function App() {
return (
<div className="App">
<div>APP React</div>
<HoxRoot>
<>
<AppRouter></AppRouter>
</>
</HoxRoot>
</div>
);
}
export default App;
改变一下 TestHox
import { useHoxStore } from "@/hox/index.hox";
const Child_1 = () => {
const { useCounterStore } = useHoxStore();
const CounterStore = useCounterStore;
return (
<div>
<p>Child_1</p>
<button onClick={() => CounterStore.increment()}>increment</button>
</div>
);
};
const Child_2 = () => {
const { useCounterStore, useUserStore } = useHoxStore();
const CounterStore = useCounterStore;
return (
<div>
<p>Child_2 ---</p>
<p>CounterStore.count:{CounterStore.count}</p>
<p>useUserStore.userAge:{useUserStore.userAge}</p>
<p>useUserStore.userName:{useUserStore.userName}</p>
<p>Child_2 ---</p>
</div>
);
};
const Child_3 = () => {
const HoxStore = useHoxStore();
const { useUserStore } = HoxStore;
return (
<div>
<p>Child_3</p>
<p>{useUserStore.userAge}</p>
<p>{useUserStore.userName}</p>
<input type="number" value={useUserStore.user.age} onChange={useUserStore.handleInputUserAge} />
</div>
);
};
function TestHox() {
return (
<>
<Child_1></Child_1>
<Child_2></Child_2>
<Child_3></Child_3>
</>
);
}
export default TestHox;
上面的代码中我们把 counter.hox.ts 的函数直接返回出去,user.hox.ts同理。
在index.hox.ts 创建一个 createGlobalStore,他接收一个返回函数。
import { createGlobalStore } from "hox";
import { CounterStore } from "./counter.hox";
import { UserStore } from "./user.hox";
const [useHoxStore, getHoxStore] = createGlobalStore(() => ({
useCounterStore: CounterStore(),
useUserStore: UserStore(),
}));
export { useHoxStore, getHoxStore };
我将user和counter放进这个返回函数里,将他们合成一个对象返回出去。
然后就能在组件中使用 /src/hox/index.hox.ts 返回出来的 useHoxStore 取出里面的数据和函数了。
但是能不能直接修改它的值而不使用hox返回的修改函数呢?
我把 Child__2和3 改一下。
const Child_2 = () => {
const { useCounterStore, useUserStore } = useHoxStore();
// const { useCounterStore, useUserStore } = useHoxStore();
const CounterStore = useCounterStore;
useEffect(() => console.log("Child_2", useUserStore.user), [useUserStore.user.name]);
return (
<div>
<p>Child_2 ---</p>
<p>CounterStore.count:{CounterStore.count}</p>
<p>useUserStore.userAge:{useUserStore.userAge}</p>
<p>useUserStore.userName:{useUserStore.userName}</p>
<p>Child_2 ---</p>
</div>
);
};
const Child_3 = () => {
const HoxStore = useHoxStore();
const { useUserStore } = HoxStore;
const handleUserName = () => {
useUserStore.user.name = "海星吧帅的一批";
console.log(useUserStore.user);
};
useEffect(() => console.log("Child_3", useUserStore.user), [useUserStore.user.name]);
return (
<div>
<p>Child_3</p>
<p>{useUserStore.userAge}</p>
<p>{useUserStore.userName}</p>
<input type="number" value={useUserStore.user.age} onChange={useUserStore.handleInputUserAge} />
<br />
<button onClick={handleUserName}>海星吧帅的一批</button>
</div>
);
};
上面我用 useEffect 跟踪 user.name 数据的更改。
现在有一个函数,他告诉你们我帅的一批,然后点击看看。
NoNoNo,虽然这个数据更改了,但是有这么一个问题,
那就是Child__3组件内把数据改了,但是并没有更新视图。
但是我触发age的更改时会发生什么。
好家伙,这回它又把之前更新的数据返回出去了。
现在页面上就显示我帅的一批,并且年龄是25,但这是不应该的。
所以别把状态内的数据直接更改比较好,引发这样的问题会很难搞。
非常好,你看到这里说明你应该学会了基础的使用了,还不会就去敲代码。
react 使用 hox 来进行状态更新是非常方便的,只不过 hox.js 还是太轻量了。
虽然简便,但是能够直接更改状态值还是不妥,应该在编写的时候就报错还是比较好。
当然,这个库的 createGlobalStore 返回的第二个值是什么意思我还是没搞懂,
我写在组件内一直编译警告,还有一个 withStore 说是让类组件也能用,因为 hox 目前是用在函数式组件里的。
createStore 配置项里的 memo,没想出怎么个使用场景,有想法再更新一下吧。
补充一下,编写代码时请注意,我用的是 hox.js 的 2.1.0 版本,