使用React responsible动态设置React Media Queries


React组件(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;
};

最新更新