Context
如果是使用过React的肯定已经理解,Context是可以跨越子孙组件传递数据。至于具体的情况下有很多,主题切换,多语言支持,表单类组件(如RadioGroup和Radio的关系)。不要为了传值方便而使用Context,不然容易造成严重的耦合。用法就以RadioGroup进行举例:
import {createSignal, createContext, useContext, JSX, Accessor} from "solid-js";
const RadioContext = createContext<{ value: Accessor<any>, onChange(value: any): void }>();
function RadioGroup(props: { value: any, children: JSX.Element, onChange?(value: any): void }) {
const value = () => props.value
const onChange = (value: any) => {
props.onChange?.(value)
}
return (
<RadioContext.Provider value={{value, onChange}}>
{props.children}
</RadioContext.Provider>
)
}
function useRadioGroupContext() {
return useContext(RadioContext)
}
function Radio(props: { value?: any, selected?: boolean, onChange?(selected: boolean): void }) {
const context = useRadioGroupContext()
const checked = () =>
context?.value ? props.value === context.value() : props.selected
const handleChange = () => {
if (context?.onChange) {
context.onChange(props.value)
} else {
props.onChange?.(!props.selected)
}
}
return (
<input type="radio" checked={checked()}
onClick={handleChange}/>
)
}
function App() {
const [selected, setSelected] = createSignal(false)
const [value, setValue] = createSignal(1)
return (
<div>
<div>
<RadioGroup value={value()} onChange={value => setValue(value)}>
<div>---分组---</div>
<div style={{display: "flex", "flex-direction": "column"}}>
<div>
<Radio value={1}/>
<span>1.java</span>
</div>
<div>
<Radio value={2}/>
<span>2.javascript</span>
</div>
<div>
<Radio value={3}/>
<span>3.typescript</span>
</div>
</div>
</RadioGroup>
</div>
<div>
<div>---单独使用---</div>
<Radio selected={selected()} onChange={selected => setSelected(selected)}/>
<span>启用</span>
</div>
</div>
)
}
用法其实与React基本类似,不一样的点在于,context的value中数据要使用响应式的数据。这个例子其实就很好的说明了,使用context的场景。
组件之间要有明确的数据传递
组件之间不一定是父子关系
数据总控制在组件树的一个共同父节点上
批量更新
SolidJS的响应式数据,与Effect之间是直接调用的。也就是当我们直接调用Setter的时候,就会立即调用所有的Effect函数。
那就会有个问题,导致如果调用多个不同的Setter,然后界面上又同时使用了(如衍生Signal中),这几个Setter对应的数据,就会导致界面多次更新。
不过这其实一般都没有问题,只是有些时候我们还是希望好几个Setter都设置之后再进行界面更新
那就要用到batch函数
const updateNames = () => {
console.log("Button Clicked");
batch(() => {
setFirstName(firstName() + "n");
setLastName(lastName() + "!");
})
}
这里就直接引用官方的例子,batch的参数接收的是一个函数,函数里面就包含了所有的setter。当所有的setter执行完之后才会执行它们所对应的Effect函数。
untrack
const [a, setA] = createSignal(1);
const [b, setB] = createSignal(1);
createEffect(() => { console.log(a(), untrack(b)); });
untrack这函数就是顾名思义,不跟踪。untrack需要传入的是函数,并且可以简单的理解为立即执行(其实要先执行一些准备工作),所以会在控制台同时打印出1,1。因为不跟踪,所以只有在a改变的时候才会触发Effect,b不管更改多少次都不会执行Effect。 虽然大家肯定都懂,在Effect执行的时候获取的b必定是最新值。
on
与untrack刚好相反,是当指定的响应式数据变化的时候才执行
const [a, setA] = createSignal(1);
const [b, setB] = createSignal(1);
const [c, setC] = createSignal(1);
createEffect(on([a, c], (data) => {
console.log(data, b());
}, {defer: true}));
上面这样写就是指定当a或c变化的时候才执行。其中data就是当前a、c的值,是一个数组形式就是on的第一个参数[a, c]这样这个样子。另外还可以看到,有个defer参数,这个参数的意思就是不立即执行(createEffect是立即执行的),当a或c出现改变的时候才执行。