在 ComponentDidMount 之后将 React.Context 传递给 Nextjs?
Posted
技术标签:
【中文标题】在 ComponentDidMount 之后将 React.Context 传递给 Nextjs?【英文标题】:Pass React.Context to Nextjs after ComponentDidMount? 【发布时间】:2020-08-27 01:59:18 【问题描述】:我有一个问题,我有一个简单的 React.Context,它在所有组件安装之后 填充。问题是因为它发生在挂载之后,nextjs 在初始渲染时看不到这个数据,所以有明显的闪烁。
这是设置上下文的简单组件:
export const SetTableOfContents = (props: item: TableOfContentsItem ) =>
const toc = useContext(TableOfContentsContext);
useEffect(() =>
// Updates the React.Context after the component mount
// (since useEffects run after mount)
toc.setItem(props.item);
, [props.item, toc]);
return null;
;
这里是 React.Context。它使用 React 状态来存储 TOC 项。
export const TableOfContentsProvider = (props:
children?: React.ReactNode;
) =>
const [items, setItems] = useState<TableOfContents["items"]>([]);
const value = useMemo(() =>
return
items,
setItem(item: TableOfContentsItem)
setItems((items) => items.concat(item));
,
;
, [items]);
return (
<TableOfContentsContext.Provider value=value>
props.children
</TableOfContentsContext.Provider>
);
;
目前,无法设置 React.Context before 挂载,因为 React 会发出警告---无法在渲染时更新状态。
我能想到的唯一解决方法是使用 除了 React.state 之外的其他东西作为 React.Context 状态——这样组件就可以随时更新它。但是这种方法的问题是上下文消费者将不再知道项目发生了变化(因为更新存在于 React 生命周期之外)!
那么如何让初始的 React.Context 进入初始的 s-s-r 渲染呢?
const items = [];
export const TableOfContentsProvider = (props:
children?: React.ReactNode;
) =>
const value = useMemo(() =>
return
items,
setItem(item: TableOfContentsItem)
items[item.index] = item;
,
;
// this dep never changes.
// when you call this function, values never change
, [items]);
return (
<TableOfContentsContext.Provider value=value>
props.children
</TableOfContentsContext.Provider>
);
;
【问题讨论】:
在getInitialProps
做你想做的事的好地方
你无权访问那里的上下文@EranGoldin
对。那这个怎么样? ***.com/questions/54709299/…
虽然不是很感谢@EranGoldin。发表了我最终做了什么
【参考方案1】:
这是我最终做的:
使用renderToString
在getStaticProps 中渲染应用程序
在上下文中使用useRef
代替useState
这样做的原因是因为renderToString
只呈现初始状态。因此,如果您使用 useState
更新 Context,它将不会捕获后续渲染
由于上述原因在组件初始化时更新上下文
将“逃生舱口”传递给上下文——我们可以调用该函数来获取在初始渲染时计算的状态
是的,整个事情看起来就像一个巨大的黑客! :-) 我不确定 React.Context 是否与 s-s-r 配合得很好 :(
export const TableOfContentsProvider = (props:
initialItems?: TableOfContentsItem[];
setItemsFors-s-r?: (items: TableOfContentsItem[]) => void;
children?: React.ReactNode;
) =>
// use useRef for the reasons mentioned above
const items = useRef(props.initialItems || []);
// Client still needs to see updates, so that's what this is for
const [count, setCount] = useState(0);
const setItemsFors-s-r = props;
const setterValue = useMemo(
() => (
setItem(item: TableOfContentsItem)
if (!items.current.find((x) => x.id === item.id))
items.current.push(item);
items.current.sort((a, b) => a.index - b.index);
setCount((count) => count + 1);
setItemsFors-s-r?.(items.current);
,
),
[setItemsFors-s-r]
);
const stateValue = useMemo(() => ( items: items.current, count ), [count]);
return (
<TableOfContentsSetterContext.Provider value=setterValue>
<TableOfContentsStateContext.Provider value=stateValue>
props.children
</TableOfContentsStateContext.Provider>
</TableOfContentsSetterContext.Provider>
);
;
interface TableOfContentsSetterWorkerProps
item: TableOfContentsItem;
setItem: (item: TableOfContentsItem) => void;
export class TableOfContentsSetterWorker extends React.Component<
TableOfContentsSetterWorkerProps,
>
constructor(props: TableOfContentsSetterWorkerProps)
super(props);
// Need to do this on init otherwise renderToString won't record it
props.setItem(props.item);
render()
return null;
/**
* Usage: use this as a child component when the parent needs to set the TOC.
*
* Exists so that a component can set the TOC without triggering
* an unnecessary render on itself.
*/
export function TableOfContentsSetter(props: item: TableOfContentsItem )
const setItem = useContext(TableOfContentsSetterContext);
return <TableOfContentsSetterWorker item=props.item setItem=setItem />;
export const getStaticProps = async () =>
let initialTableOfContents: TableOfContentsItem[] = [];
const getItems = (items: TableOfContentsItem[]) =>
initialTableOfContents = [...items];
;
const app = () => (
<TableOfContentsProvider setItemsFors-s-r=getItems>
<AppArticles />
</TableOfContentsProvider>
);
renderToString(app());
return
props:
initialTableOfContents,
,
;
;
【讨论】:
以上是关于在 ComponentDidMount 之后将 React.Context 传递给 Nextjs?的主要内容,如果未能解决你的问题,请参考以下文章
componentWillMount和componentDidMount的区别
react请求接口数据是在componentDidMount 还是componentWillMount周期好
React componentDidMount 获取 api