Viewer
(包含元素的动态列表(Element
(。元素的大小取决于当前屏幕分辨率。
屏幕分辨率配置动态传递给Viewer
组件,如下所示:
<Viewer
elements={[{
uid: '1',
title: 'project 1'
}, {
uid: '2',
title: 'project 2'
}, {
uid: '3',
title: 'project 3'
}, {
uid: '4',
title: 'project 4'
}]}
configurations={[
{
name: 'large screen',
width: 1200,
gridRowSize: 4
},
{
name: 'tablet',
width: 800,
gridRowSize: 2
},
{
name: 'phone',
width: 480,
gridRowSize: 1
}
]}/>
configurations
属性是动态的,Viewer
组件不能对其进行任何假设,因为它来自RESTneneneba API。配置和元素彼此不相关。
我使用react-responsive
来实现如下结果:
import {useMediaQuery} from 'react-responsive';
export default function Viewer(props) {
const configurations = [];
if (props.configurations) {
for (const [index, configuration] of props.configurations.sort((a, b) => (a.width - b.width)).entries()) {
configurations.push({
// props.configurations does not change between renders, so it's safe to disable the lint check
// eslint-disable-next-line react-hooks/rules-of-hooks
isActive: useMediaQuery({
...index > 0 && {minWidth: props.configurations[index - 1].width},
...index < (props.configurations.length - 1) && {maxWidth: configuration.width}
}),
size: 100 / configuration.gridRowSize
});
}
}
const activeConfiguration = configurations.find(config => config.isActive);
const size = activeConfiguration ? activeConfiguration.size : 25;
return (
<div className={'viewer'}>
{
props.elements && props.elements.map((element) => (
<Element
key={element.uid}
id={element.uid}
title={element.title}
size={`${size}%`}/>
))
}
</div>
);
}
问题是上面的实现破坏了这里描述的Hooks规则之一:https://reactjs.org/docs/hooks-rules.html#eslint-插件(React依赖于调用Hook的顺序,理论上循环可以打破这个顺序(。
在不禁用lint检查的情况下,我在运行eslint
:时会出现此错误
React Hook "useMediaQuery" may be executed more than once. Possibly because it is called in a loop.
React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
我目前正在禁用lint错误检查,因为props.configurations
在渲染之间不会更改,但我想知道是否有更好的方法可以使用react-responsive
配置动态媒体查询?
我没有使用react-responsive
,但我想我理解这个问题。您有一个道具(configs(的动态列表,每个道具都需要传递给Hook,Hook将定义要传递给组件(Element(的特定属性(大小(。问题是,所有这些逻辑都不应该在单个父组件(Viewer(中处理。
如果您可以使用useMediaQuery
的功能,这将使它更干净。如果没有,您可以为每个配置元素创建一个具有自己Hook的组件。下面的代码假设始终存在匹配的查询。如果不是这样,请告诉我。
export const Viewer = props => {
const { elements, configurations } = props;
const [active_size, setActiveSize] = useState(25);
return (
<div className={"viewer"}>
{configurations.map((configuration, index) => (
<Configuration
key={configuration.name}
{...configuration}
index={index}
setActiveSize={setActiveSize}
configurations={configurations}
/>
))}
{elements.map(({ uid, title }) => (
<Element key={uid} id={uid} title={title} size={`${active_size}%`} />
))}
</div>
);
};
export const Configuration = props => {
const { width, configurations, index, setActiveSize, gridRowSize } = props;
const is_active = useMediaQuery({
...(index > 0 && { minWidth: configurations[index - 1].width }),
...(index < configurations.length - 1 && {
maxWidth: width
})
});
useEffect(() => {
if (is_active) {
setActiveSize(100 / gridRowSize);
}
}, [is_active]);
return null;
};