reactjs让列表组件单独观察上下文
当我们让每个组件观看自己的状态
)例如 codepen
const DataContext = React.createContext();
const DataReducer = function (state, action) {
switch (action.method) {
case "add":
state[action.id] = action.params;
break;
case "delete":
delete state[action.id];
break;
default:
}
return { ...state };
};
const DataContextProvider = function (props) {
let [data, setData] = React.useReducer(DataReducer, {});
let [filter, setFilter] = React.useState(null);
let value = {
data: data,
setData: setData,
filter: filter,
setFilter: setFilter
};
return (
<DataContext.Provider value={value}>{props.children}</DataContext.Provider>
);
};
const DelayInput = function (props) {
let [timer, setTimer] = React.useState(null);
let delay = props.delay ? props.delay : 1000;
let delayChange = (e) => {
if (timer) {
clearTimeout(timer);
setTimer(null);
}
setTimer(
setTimeout(() => {
props.onChange(e.target);
}, delay)
);
};
return (
<input
className="input"
type="text"
placeholder={props.placeholder}
defaultValue={props.value}
onChange={delayChange}
name={props.name}
/>
);
};
const Tool = function (props) {
let { setData, setFilter } = React.useContext(DataContext);
let handleChange = ((e) => {
setFilter(e.value);
});
let handleClick = () => {
setData({
method: "add",
id: new Date().getTime(),
params: {
x: Math.random(),
y: Math.random(),
last: new Date().getTime()
}
});
};
return (
<div>
<button className="button" onClick={handleClick}>
Add
</button>
<DelayInput
name="filter"
placeholder="Filter"
onChange={handleChange}
/>
</div>
);
};
const Row = function (props) {
let { data, filter, setData } = React.useContext(DataContext);
let handleClick = (() => {
setData({
method: "delete",
id: props.id
});
});
return React.useMemo(() => {
if (filter && !props.id.toString().includes(filter)) {
return null;
}
return (
<tr>
<td>{props.id}</td>
<td>{data[props.id].x}</td>
<td>{data[props.id].y}</td>
<td>{data[props.id].last}</td>
<td>
<button className="button is-narrow" onClick={handleClick}>
Delete
</button>
</td>
</tr>
);
}, [filter, data[props.id]]);
};
const TableContent = function (props) {
let { data } = React.useContext(DataContext);
return React.useMemo(() => {
return Object.keys(data).map((i) => {
return <Row key={i} id={i} />;
});
}, [Object.keys(data)]);
};
const Container = function (props) {
return (
<DataContextProvider>
<Tool />
<table className="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
<tr>
<th>ID</th>
<th>X</th>
<th>Y</th>
<th>Last Update</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<TableContent />
</tbody>
</table>
</DataContextProvider>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<Container />);
我的理解,我可以确保我们可以确保这个组件只关心他的状态,像观察者模式和责任分离。
状态内在上下文是数据
该组件成为所选状态的观察者并相应地更改为它,在我的示例中,如果 row 组件
行组件也可以观察 filter 值,如果滤波器中的值更改且本身不匹配过滤器,然后它会隐藏自身。
而且,由于该行观看了自己的状态,我们可以自由地记住该组件
是否有这种方法有任何缺点吗?
is there any performance impact or other disadvantage when we let each component to watch their own state
For example codepen
const DataContext = React.createContext();
const DataReducer = function (state, action) {
switch (action.method) {
case "add":
state[action.id] = action.params;
break;
case "delete":
delete state[action.id];
break;
default:
}
return { ...state };
};
const DataContextProvider = function (props) {
let [data, setData] = React.useReducer(DataReducer, {});
let [filter, setFilter] = React.useState(null);
let value = {
data: data,
setData: setData,
filter: filter,
setFilter: setFilter
};
return (
<DataContext.Provider value={value}>{props.children}</DataContext.Provider>
);
};
const DelayInput = function (props) {
let [timer, setTimer] = React.useState(null);
let delay = props.delay ? props.delay : 1000;
let delayChange = (e) => {
if (timer) {
clearTimeout(timer);
setTimer(null);
}
setTimer(
setTimeout(() => {
props.onChange(e.target);
}, delay)
);
};
return (
<input
className="input"
type="text"
placeholder={props.placeholder}
defaultValue={props.value}
onChange={delayChange}
name={props.name}
/>
);
};
const Tool = function (props) {
let { setData, setFilter } = React.useContext(DataContext);
let handleChange = ((e) => {
setFilter(e.value);
});
let handleClick = () => {
setData({
method: "add",
id: new Date().getTime(),
params: {
x: Math.random(),
y: Math.random(),
last: new Date().getTime()
}
});
};
return (
<div>
<button className="button" onClick={handleClick}>
Add
</button>
<DelayInput
name="filter"
placeholder="Filter"
onChange={handleChange}
/>
</div>
);
};
const Row = function (props) {
let { data, filter, setData } = React.useContext(DataContext);
let handleClick = (() => {
setData({
method: "delete",
id: props.id
});
});
return React.useMemo(() => {
if (filter && !props.id.toString().includes(filter)) {
return null;
}
return (
<tr>
<td>{props.id}</td>
<td>{data[props.id].x}</td>
<td>{data[props.id].y}</td>
<td>{data[props.id].last}</td>
<td>
<button className="button is-narrow" onClick={handleClick}>
Delete
</button>
</td>
</tr>
);
}, [filter, data[props.id]]);
};
const TableContent = function (props) {
let { data } = React.useContext(DataContext);
return React.useMemo(() => {
return Object.keys(data).map((i) => {
return <Row key={i} id={i} />;
});
}, [Object.keys(data)]);
};
const Container = function (props) {
return (
<DataContextProvider>
<Tool />
<table className="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
<tr>
<th>ID</th>
<th>X</th>
<th>Y</th>
<th>Last Update</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<TableContent />
</tbody>
</table>
</DataContextProvider>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<Container />);
My understanding that with this we can make sure that this one component only care about his state, something like observer pattern and separation of responsibility.
The state inside context is the data
The component become observer of the chosen state and change accordingly to it, in my example is Row component
Row component also observe filter value, if the value in the filter change and itself doesn't match the filter then it will hide itself.
And since the row watch his own state, we can freely memoize the component
Is there any disadvantage for this kind of approach ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因此,当您使用这样的上下文时,只要数据的任何部分都更改,使用上下文的每个子组件都会恢复。
这意味着,即使
data [props.id]
没有更改,如果data
更改的任何部分,它也会恢复。我建议改用Redux。实际上,我前几天第一次亲自使用它,这比我预期的要容易。与
USECONTEXT
不同,它允许您订阅状态的一小部分,并且只有在您依赖更改的状态时才能订阅。参见 useselector 有关详细信息。So when you're using context like this, each child component that uses the context will rerender whenever any part of the data changes.
This means that even if
data[props.id]
doesn't change, it will rerender if any part ofdata
changes.I'd recommend using Redux instead. I actually just used it for myself the other day for the first time, and it was easier than I expected. Unlike
useContext
, it allows you to subscribe to a tiny slice of the state, and only rerender when the state you depend on changes. See useSelector for details.如果正确使用上下文API,则只有那些消耗上下文的组件在更改时会重新渲染。我建议您查看 react-context-slices ,它是一个库它以最佳方式使用了反应上下文,并允许您轻松地和0样式板定义上下文切片。通过这种方式,只有获取每个上下文片的组件才能在其价值变化时进行更新。您这样做:
希望这可以帮助任何计划使用上下文的人。
If Context API is used properly, then only those components consuming Context will re-render when this changes. I recommend you take a look at react-context-slices, it's a library that uses React Context in an optimal way and allows you to define slices of Context easily and with 0 boilerplate. In this manner, only the components fetching each slice of Context will update when its value change. You do it like this:
Hope this helps anyone planning to use Context.
嗨,经过调整后,这是我修复它的方式
codepen> codepen
通过创建一个额外的状态,以保持活跃(选定的项目)
在更新时,我必须更新表单和此状态。
目前没有任何问题,认为感觉有点像黑客...
Hi after tweaking it this is how I fix it
Codepen
By creating an additional state that hold the active ( selected item )
On update I have to update both form and this state.
Currently doesn't have any problem, thought it feel a bit like a hack...