Javascript 如何在App shell和SSR中实现同级组件通信
我采纳了一个建立在这个基础上的项目。该架构采用应用程序外壳和SSR。我正在尝试添加一个简单的搜索栏,这意味着将搜索键从搜索栏组件传递到帖子列表组件,以便对其进行过滤。我发现,对于上下文提供者和消费者来说,这几乎是不可能的。我想使用上下文,但我不知道怎么做。看起来这个初学者工具包有一个严重的缺点,如果能够解决,它将使这个工具包在网上更有用 如果您查看下面的代码和上面的链接,您可以看到有一个标题中心,然后是页面。我需要在页眉和页面之间进行通信。您可以使用链接中的代码添加同级通信 水合物的使用似乎排除了上下文提供者的简单应用。Hydrome以并行方式添加组件,而没有办法让上下文提供程序位于这两个组件之上。我在这里使用的模式不起作用。当我更新提供者时,它不会导致上下文使用者的重新呈现 如果我不得不使用上下文以外的东西,比如说Redux,那么我会接受这个答案 以下是客户端入口点:Javascript 如何在App shell和SSR中实现同级组件通信,javascript,reactjs,Javascript,Reactjs,我采纳了一个建立在这个基础上的项目。该架构采用应用程序外壳和SSR。我正在尝试添加一个简单的搜索栏,这意味着将搜索键从搜索栏组件传递到帖子列表组件,以便对其进行过滤。我发现,对于上下文提供者和消费者来说,这几乎是不可能的。我想使用上下文,但我不知道怎么做。看起来这个初学者工具包有一个严重的缺点,如果能够解决,它将使这个工具包在网上更有用 如果您查看下面的代码和上面的链接,您可以看到有一个标题中心,然后是页面。我需要在页眉和页面之间进行通信。您可以使用链接中的代码添加同级通信 水合物的使用似乎排除
import { onPageLoad } from 'meteor/server-render';
import MeteorLoadable from 'meteor/nemms:meteor-react-loadable';
import { Switch, Route, Router, Redirect } from 'react-router-dom';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { onError } from 'apollo-link-error';
import apolloLogger from 'apollo-link-logger';
import { onTokenChange, getLoginToken } from '/app/ui/apollo-client/auth';
import createBrowserHistory from 'history/createBrowserHistory';
import 'unfetch/polyfill';
// Initialise react-intl
import { primaryLocale, otherLocales } from '/app/intl';
// Need to preload of list of loadable components for MeteorLoadable
import '/app/ui/loadables';
// To get started, create an ApolloClient instance and point it at your GraphQL
// server. By default, this client will send queries to the '/graphql' endpoint
// on the same host.
// To avoid asynchronously accessing local storage for every GraphQL request,
// we cache the authorisation token, and update it using an onTokenChange callback
let authToken;
let authTokenInitialised = false;
onTokenChange(({ token }) => { authToken = token; authTokenInitialised = true; });
const withAuthToken = setContext(() => {
if (authTokenInitialised) {
return authToken ? { headers: { authorization: authToken } } : undefined;
}
return getLoginToken()
.then((token) => {
authToken = token;
authTokenInitialised = true;
return authToken ? { headers: { authorization: authToken } } : undefined;
});
});
const resetAuthToken = onError(({ networkError }) => {
if (networkError && networkError.statusCode === 401) {
// Remove cached token on 401 from the server
authToken = null;
authTokenInitialised = false;
}
});
const onErrorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) => console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
));
}
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const client = new ApolloClient({
link: ApolloLink.from([
apolloLogger,
withAuthToken,
resetAuthToken,
onErrorLink,
new HttpLink({
uri: '/graphql',
}),
]),
cache: new InMemoryCache().restore(window.__APOLLO_STATE__),
});
// Inject the data into the app shell.
// If the structure's changed, ssr.js also needs updating.
async function renderAsync() {
const [
React,
{ hydrate, render },
{ default: App },
{ default: HeaderTitle },
{ default: LanguagePicker },
{ default: Routes },
{ default: Menu },
] = await Promise.all([
import('react'),
import('react-dom'),
import('/app/ui/components/smart/app'),
import('/app/ui/components/smart/header/header-title'),
import('/app/ui/components/dumb/language-picker'),
import('/app/ui/routes'),
import('/app/ui/components/smart/menu'),
MeteorLoadable.preloadComponents(),
]);
// Given that we are implementing App Shell Architecture and, therefore,
// injecting (via reactDOM.render) the Header, Menu and Main components into
// different HTML elements, we need a way to share the router 'history' among
// all three mentioned components.
// As a default, for every invocation of 'BrowserRouter', there will be new
// 'history' instance created. Then, changes in the 'history' object in one
// component won't be available in the other components. To prevent this, we are
// relying on the 'Router' component instead of 'BrowserRouter' and defining our
// custom 'history' object by means of 'createBrowserHistory' function. Said
// 'history' object is then passed to every invocation of 'Router' and therefore
// the same 'history' object will be shared among all three mentioned components.
const history = createBrowserHistory();
// Inject react app components into App's Shell
const ClientApp = ({ component }) => (
<Router history={history}>
<ApolloProvider client={client}>
<Switch>
{/* Map our locales to separate routes */}
{ otherLocales.map(locale => (
<Route
key={locale}
path={`/${locale}/`}
render={props => <App component={component} {...props} locale={locale} section="app" />}
/>
))}
{ primaryLocale && (
<Route
key={primaryLocale}
path="/"
render={props => <App component={component} {...props} locale={primaryLocale} section="app" />}
/>
)}
{/* If no valid locale is given, we redirect to same route with the preferred locale prefixed */}
<Route render={({ location }) => <Redirect to={`/${window.__PREFERRED_LOCALE__ || otherLocales[0]}${location.pathname}`} />} />
</Switch>
</ApolloProvider>
</Router>
);
render(<ClientApp component={Menu} />, document.getElementById('menu'));
hydrate(<ClientApp component={HeaderTitle} />, document.getElementById('header-title'));
hydrate(<ClientApp component={LanguagePicker} />, document.getElementById('header-lang-picker'));
hydrate(<ClientApp component={Routes} />, document.getElementById('main'));
}
onPageLoad(() => {
const renderStart = Date.now();
const startupTime = renderStart - window.performance.timing.responseStart;
console.log(`Meteor.startup took: ${startupTime}ms`);
// Register service worker
import('/app/ui/register-sw').then(() => {});
renderAsync().then(() => {
const renderTime = Date.now() - renderStart;
console.log(`renderAsync took: ${renderTime}ms`);
console.log(`Total time: ${startupTime + renderTime}ms`);
});
});
从'meteor/server render'导入{onPageLoad};
从“meteor/nemms:meteor react loadable”导入MeteorLoadable;
从“react Router dom”导入{交换机、路由、路由器、重定向};
从“apollo客户端”导入{apollo客户端};
从'react apollo'导入{ApolloProvider};
从'apollo cache inmemory'导入{InMemoryCache};
从“阿波罗链接”导入{apollo链接};
从“阿波罗链接http”导入{HttpLink};
从“阿波罗链接上下文”导入{setContext};
从“阿波罗链接错误”导入{onError};
从“阿波罗链接记录器”导入阿波罗记录器;
从'/app/ui/apollo-client/auth'导入{onTokenChange,getLoginToken};
从“历史记录/createBrowserHistory”导入createBrowserHistory;
导入“未蚀刻/聚填充”;
//初始化react intl
从'/app/intl'导入{primaryLocale,otherLocales};
//需要为可加载组件预加载可加载组件列表
导入“/app/ui/loadables”;
//首先,创建一个Apollo客户端实例并将其指向GraphQL
//服务器。默认情况下,此客户端将向“/graphql”端点发送查询
//在同一主机上。
//为了避免对每个GraphQL请求异步访问本地存储,
//我们缓存授权令牌,并使用onTokenChange回调更新它
让authonk;
让AuthTokenInitialized=false;
onTokenChange(({token})=>{authToken=token;AuthTokenInitialized=true;});
const withAuthToken=setContext(()=>{
如果(已初始化){
返回authToken?{头:{授权:authToken}}:未定义;
}
返回getLoginToken()
。然后((令牌)=>{
authToken=令牌;
AuthTokenInitialized=true;
返回authToken?{头:{授权:authToken}}:未定义;
});
});
const resetAuthToken=onError({networkError})=>{
if(networkError&&networkError.statusCode==401){
//从服务器中删除401上的缓存令牌
authToken=null;
AuthTokenInitialized=false;
}
});
const onErrorLink=onError({graphQLErrors,networkError})=>{
如果(图形错误){
graphQLErrors.map({message,locations,path})=>console.log(
`[GraphQL错误]:消息:${Message},位置:${locations},路径:${Path}`,
));
}
if(networkError)console.log(`[networkError]:${networkError}`);
});
const客户端=新客户端({
链接:ApolloLink.from([
阿波罗记录器,
使用AuthToken,
重置AuthToken,
onErrorLink,
新HttpLink({
uri:“/graphql”,
}),
]),
缓存:新建InMemoryCache().restore(窗口).\uuu APOLLO\u STATE\uuuuuu),
});
//将数据注入应用程序外壳。
//如果结构发生变化,ssr.js也需要更新。
异步函数renderAsync(){
常数[
反应
{水合物,渲染},
{default:App},
{default:HeaderTitle},
{default:LanguagePicker},
{default:Routes},
{默认:菜单},
]等待承诺([
导入('react'),
导入('react-dom'),
导入('/app/ui/components/smart/app'),
导入('/app/ui/components/smart/header/header title'),
导入('/app/ui/components/dumb/language picker'),
导入('/app/ui/routes'),
导入('/app/ui/components/smart/menu'),
MeteorLoadable.preloadComponents(),
]);
//鉴于我们正在实施应用程序外壳架构,因此,
//将标题、菜单和主要组件(通过reactDOM.render)注入到
//不同的HTML元素,我们需要一种方法来共享路由器的“历史”
//上述三个组成部分。
//默认情况下,每次调用“BrowserRouter”都会有新的
//创建了“history”实例。然后,在一个实例中更改“history”对象
//组件在其他组件中不可用。为防止此情况,我们
//依赖“路由器”组件而不是“浏览器路由器”,并定义
//通过“createBrowserHistory”函数自定义“history”对象
//然后将“历史”对象传递给“路由器”的每次调用,因此
//上述三个组件将共享同一个“历史”对象。
const history=createBrowserHistory();
//将react应用程序组件注入应用程序外壳
常量ClientApp=({component})=>(
{/*将我们的地区映射到不同的路线*/}
{otherLocales.map(locale=>(
}
/>
))}
{primaryLocale&&(
}
/>
)}
{/*若并没有给出有效的语言环境,我们将重定向到带有首选语言环境前缀的相同路由*/}
} />
);
render(,document.getElementById('menu'));
水合物(,document.getElementById('header-title'