MST

星途 面试题库

面试题:React Context在服务端渲染高级难度题

假设在一个复杂的React应用进行服务端渲染时,有多个相互嵌套的组件需要通过Context共享数据,并且这些组件在客户端渲染时可能会发生更新。请阐述如何避免Context在服务端渲染和客户端渲染不一致的问题,以及如何优化Context数据传递以减少性能损耗。
18.7万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

避免Context在服务端渲染和客户端渲染不一致的问题

  1. 使用静态方法获取数据 在需要使用Context数据的组件中,使用getStaticProps(对于Next.js)或类似的服务端数据获取方法。这样可以在服务端渲染阶段就获取到准确的数据,避免客户端渲染时重新获取导致不一致。例如:
export async function getStaticProps() {
  const contextData = await fetchContextData();
  return {
    props: {
      contextData
    },
    revalidate: 60 // 可选,用于增量静态再生
  };
}

const MyComponent = ({ contextData }) => {
  // 使用contextData
  return <div>{contextData.value}</div>;
};
  1. 初始化Context数据 在服务端渲染和客户端渲染开始时,确保Context数据的初始值一致。可以将初始数据作为props传递给顶层组件,然后由顶层组件设置Context。例如:
const App = ({ initialContextData }) => {
  const [contextData, setContextData] = useState(initialContextData);
  return (
    <Context.Provider value={{ contextData, setContextData }}>
      {/* 子组件 */}
    </Context.Provider>
  );
};

// 服务端渲染时传递初始数据
const initialData = { value: 'initial value' };
ReactDOMServer.renderToString(<App initialContextData={initialData} />);

// 客户端渲染时传递相同的初始数据
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App initialContextData={initialData} />);
  1. 数据序列化与反序列化 如果Context数据包含复杂对象,在服务端渲染时将数据序列化,在客户端渲染时反序列化。这样可以确保数据在两端以相同的形式存在。例如:
// 服务端
const contextData = { complexObject: { key: 'value' } };
const serializedData = JSON.stringify(contextData);
const html = ReactDOMServer.renderToString(<App initialContextData={serializedData} />);

// 客户端
const root = ReactDOM.createRoot(document.getElementById('root'));
const { initialContextData } = window.__INITIAL_PROPS__;
const deserializedData = JSON.parse(initialContextData);
root.render(<App initialContextData={deserializedData} />);

优化Context数据传递以减少性能损耗

  1. 减少不必要的Context更新 使用useMemouseCallback来包裹Context值和函数,防止不必要的重新渲染。例如:
const contextValue = useMemo(() => ({
  data: someData,
  updateData: useCallback(() => {
    // 更新逻辑
  }, [])
}), [someData]);

return (
  <Context.Provider value={contextValue}>
    {/* 子组件 */}
  </Context.Provider>
);
  1. 使用Context分层 将Context按照功能或数据类型进行分层,避免一个大而全的Context。这样可以减少因某个小数据变化导致的大量组件重新渲染。例如:
const UserContext = createContext();
const ThemeContext = createContext();

const App = () => {
  const user = useSelector(state => state.user);
  const theme = useSelector(state => state.theme);

  return (
    <UserContext.Provider value={user}>
      <ThemeContext.Provider value={theme}>
        {/* 子组件 */}
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
};
  1. Memoize Context Consumers 对于使用Context的子组件,使用React.memo进行包裹,只有当组件的props变化时才重新渲染。例如:
const MyChildComponent = React.memo(({ value }) => {
  return <div>{value}</div>;
});