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
组件由于来自REST 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-plugin(React依赖于调用Hooks的顺序,并且循环理论上可以破坏该顺序)。
在不禁用棉绒检查的情况下,运行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
由于props.configurations
在渲染之间不会更改,因此我目前正在禁用棉绒错误检查,但是我想知道是否有更好的方法使用react-responsive
配置动态媒体查询?
答案 0 :(得分:0)
我还没有使用react-responsive
,但我想我理解这个问题。您有一个道具(配置)的动态列表,每个道具都需要传递到一个挂钩,该挂钩将定义要传递给组件(元素)的某个属性(大小)。问题在于所有这些逻辑都不应在单个父组件(查看器)中处理。
如果可以直接使用useMediaQuery
的功能,这将使其更加整洁。如果没有,您可以为每个配置元素创建一个具有自己的挂钩的组件。下面的代码假定总会有一个匹配的查询。让我知道是否是这种情况。
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;
};