如何 show/hide spinners, snackbars, or other transient components with React Function Components
How to show/hide spinners, snackbars, or other transient components with React Function Components
在单独的 DOM 元素中呈现微调器、小吃店等是否比将它们添加到主应用程序组件树更好?在 React class 组件中,很容易将 class 组件方法的引用引用到 show/hide 微调器。有了新的 React Hooks 功能组件,这不再那么容易了。如果我将微调器放在主要组件树中,我可以使用新的 "useContext" 挂钩到 show/hide 微调器吗?
下面是一个使用 Material-UI 的 React Hooks 全局微调器,它可以工作但是非常 hacky。如何让它更优雅?
namespace Spinner {
'use strict';
export let show: any; // Show method ref.
export let hide: any; // Hide method ref.
export function Render() {
const [visible, setVisible] = React.useState(false); //Set refresh method.
function showIt() {
setVisible(true); // Show spinner.
}
function hideIt() {
setVisible(false); // Hide spinner.
}
const styles: any = createStyles({
col1Container: { display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' },
});
return (
<div>
{visible && <div style={styles.col1Container}>
<CircularProgress key={Util.uid()}
color='secondary'
size={30}
thickness={3.6}
/>
</div>}
<SetSpinnerRefs showRef={showIt} hideRef={hideIt} />
</div>
); // end return.
} // end function.
const mounted: boolean = true;
interface iProps {
showRef();
hideRef();
}
function SetSpinnerRefs(props: iProps) {
// ComponentDidMount.
React.useEffect(() => {
Spinner.show = props.showRef;
Spinner.hide = props.hideRef;
}, [mounted]);
return (<span />);
}
} // end module.
In React class components, it was really easy to get a reference to the class components methods to show/hide the spinner
您可以继续使用 class 个组件。他们哪也不去
不太好的方法
在我看来,使用 class 方法来显示和隐藏微调器实际上是一种糟糕的做法。假设你的 api 看起来像
<Spinner {ref=>this.something=ref}/>
而你使用
this.something.show(); // or .hide
更好的方法
<Spinner shown={state.shown}/>
现在您可以更改 state.shown
而不是存储 ref 并使用 show
/ hide
。
问题与 类似,微调器的解决方案与模态 windows 的解决方案相同。 React hooks 不会改变它的工作方式,但可以使它更简洁。
组件层次结构中应该有单个微调器实例:
const SpinnerContext = React.createContext();
const SpinnerContainer = props => {
const [visible, setVisible] = React.useState(false);
const spinner = useMemo(() => ({
show: () => setVisible(true),
hide: () => setVisible(false),
}), []);
render() {
return <>
{visible && <Spinner />}
<SpinnerContext.Provider value={spinner}>
{props.children}
</SpinnerContext.Provider>
</>;
}
}
与上下文一起传递:
const ComponentThatUsesSpinner = props => {
const spinner = useContext(SpinnerContext);
...
spinner.hide();
...
}
<SpinnerContainer>
...
<ComponentThatUsesSpinner />
...
</SpinnerContainer>
尽管我认为 Basarat 的答案是解决此问题的现代方法,但下面的代码是我最终采用的方法。这样我只需要一行代码来构建微调器,只需要一行代码就可以 show/hide 它。
<Spinner.Render /> {/* Build spinner component */}
Spinner.show(); //Show spinner.
namespace Spinner {
'use strict';
export let show: any; //Ref to showIt method.
export let hide: any; //Ref to hideIt method.
export function Render() {
const [visible, setVisible] = React.useState(false); //Set refresh method.
function showIt() {
setVisible(true); //Show spinner.
}
function hideIt() {
setVisible(false); //Hide spinner.
}
const showRef: any = React.useRef(showIt);
const hideRef: any = React.useRef(hideIt);
//Component did mount.
React.useEffect(() => {
Spinner.show = showRef.current;
Spinner.hide = hideRef.current;
}, []);
const styles: any = createStyles({
row1Container: { display: 'flex', alignItems: 'center', justifyContent: 'center' },
});
return (
<div>
{visible && <div style={styles.row1Container}>
<CircularProgress
color='secondary'
size={30}
thickness={3.6}
/>
</div>}
</div>
); //end return.
} //end function.
} //end module.
在单独的 DOM 元素中呈现微调器、小吃店等是否比将它们添加到主应用程序组件树更好?在 React class 组件中,很容易将 class 组件方法的引用引用到 show/hide 微调器。有了新的 React Hooks 功能组件,这不再那么容易了。如果我将微调器放在主要组件树中,我可以使用新的 "useContext" 挂钩到 show/hide 微调器吗?
下面是一个使用 Material-UI 的 React Hooks 全局微调器,它可以工作但是非常 hacky。如何让它更优雅?
namespace Spinner {
'use strict';
export let show: any; // Show method ref.
export let hide: any; // Hide method ref.
export function Render() {
const [visible, setVisible] = React.useState(false); //Set refresh method.
function showIt() {
setVisible(true); // Show spinner.
}
function hideIt() {
setVisible(false); // Hide spinner.
}
const styles: any = createStyles({
col1Container: { display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' },
});
return (
<div>
{visible && <div style={styles.col1Container}>
<CircularProgress key={Util.uid()}
color='secondary'
size={30}
thickness={3.6}
/>
</div>}
<SetSpinnerRefs showRef={showIt} hideRef={hideIt} />
</div>
); // end return.
} // end function.
const mounted: boolean = true;
interface iProps {
showRef();
hideRef();
}
function SetSpinnerRefs(props: iProps) {
// ComponentDidMount.
React.useEffect(() => {
Spinner.show = props.showRef;
Spinner.hide = props.hideRef;
}, [mounted]);
return (<span />);
}
} // end module.
In React class components, it was really easy to get a reference to the class components methods to show/hide the spinner
您可以继续使用 class 个组件。他们哪也不去
不太好的方法
在我看来,使用 class 方法来显示和隐藏微调器实际上是一种糟糕的做法。假设你的 api 看起来像
<Spinner {ref=>this.something=ref}/>
而你使用
this.something.show(); // or .hide
更好的方法
<Spinner shown={state.shown}/>
现在您可以更改 state.shown
而不是存储 ref 并使用 show
/ hide
。
问题与
组件层次结构中应该有单个微调器实例:
const SpinnerContext = React.createContext();
const SpinnerContainer = props => {
const [visible, setVisible] = React.useState(false);
const spinner = useMemo(() => ({
show: () => setVisible(true),
hide: () => setVisible(false),
}), []);
render() {
return <>
{visible && <Spinner />}
<SpinnerContext.Provider value={spinner}>
{props.children}
</SpinnerContext.Provider>
</>;
}
}
与上下文一起传递:
const ComponentThatUsesSpinner = props => {
const spinner = useContext(SpinnerContext);
...
spinner.hide();
...
}
<SpinnerContainer>
...
<ComponentThatUsesSpinner />
...
</SpinnerContainer>
尽管我认为 Basarat 的答案是解决此问题的现代方法,但下面的代码是我最终采用的方法。这样我只需要一行代码来构建微调器,只需要一行代码就可以 show/hide 它。
<Spinner.Render /> {/* Build spinner component */}
Spinner.show(); //Show spinner.
namespace Spinner {
'use strict';
export let show: any; //Ref to showIt method.
export let hide: any; //Ref to hideIt method.
export function Render() {
const [visible, setVisible] = React.useState(false); //Set refresh method.
function showIt() {
setVisible(true); //Show spinner.
}
function hideIt() {
setVisible(false); //Hide spinner.
}
const showRef: any = React.useRef(showIt);
const hideRef: any = React.useRef(hideIt);
//Component did mount.
React.useEffect(() => {
Spinner.show = showRef.current;
Spinner.hide = hideRef.current;
}, []);
const styles: any = createStyles({
row1Container: { display: 'flex', alignItems: 'center', justifyContent: 'center' },
});
return (
<div>
{visible && <div style={styles.row1Container}>
<CircularProgress
color='secondary'
size={30}
thickness={3.6}
/>
</div>}
</div>
); //end return.
} //end function.
} //end module.