【问题标题】:Access match object at navbar - React, Redux and TypeScript在导航栏访问匹配对象 - React、Redux 和 TypeScript
【发布时间】:2018-03-11 02:12:03
【问题描述】:

我需要导航栏上的匹配对象。我正在使用 React、Redux 和 TypeScript。

这是我的启动客户端文件:

    import './css/site.css';
    import 'bootstrap';
    import * as React from 'react';
    import * as ReactDOM from 'react-dom';
    import { AppContainer } from 'react-hot-loader';
    import { Provider } from 'react-redux';
    import { ConnectedRouter } from 'react-router-redux';
    import { createBrowserHistory } from 'history';
    import configureStore from './configureStore';
    import { ApplicationState } from './store/initialState';
    import * as RoutesModule from './routes';
    let routes = RoutesModule.routes;

    // Create browser history to use in the Redux store
    const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
    const history = createBrowserHistory({ basename: baseUrl });

    // Get the application-wide store instance, prepopulating with state from the server where available.
    const initialState = (window as any).initialReduxState as ApplicationState;
    const store = configureStore(history, initialState);

    function renderApp() {
        // This code starts up the React app when it runs in a browser. It sets up the routing configuration
        // and injects the app into a DOM element.
        ReactDOM.render(
            <AppContainer>
                <Provider store={ store }>
                    <ConnectedRouter history={ history } children={ routes } />
                </Provider>
            </AppContainer>,
            document.getElementById('react-app')
        );
    }

    renderApp();

    // Allow Hot Module Replacement
    if (module.hot) {
        module.hot.accept('./routes', () => {
            routes = require<typeof RoutesModule>('./routes').routes;
            renderApp();
        });
    }

This is my boot-server file:



  import * as React from 'react';
    import { Provider } from 'react-redux';
    import { renderToString } from 'react-dom/server';
    import { StaticRouter } from 'react-router-dom';
    import { replace } from 'react-router-redux';
    import { createMemoryHistory } from 'history';
    import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
    import { routes } from './routes';
    import configureStore from './configureStore';

    export default createServerRenderer(params => {
        return new Promise<RenderResult>((resolve, reject) => {
            // Prepare Redux store with in-memory history, and dispatch a navigation event
            // corresponding to the incoming URL
            const basename = params.baseUrl.substring(0, params.baseUrl.length - 1); // Remove trailing slash
            const urlAfterBasename = params.url.substring(basename.length);
            const store = configureStore(createMemoryHistory());
            store.dispatch(replace(urlAfterBasename));

            // Prepare an instance of the application and perform an inital render that will
            // cause any async tasks (e.g., data access) to begin
            const routerContext: any = {};
            const app = (
                <Provider store={ store }>
                    <StaticRouter basename={ basename }
                                  context={ routerContext }
                                  location={ params.location.path }
                                  children={ routes } />
                </Provider>
            );
            renderToString(app);

            // If there's a redirection, just send this information back to the host application
            if (routerContext.url) {
                resolve({ redirectUrl: routerContext.url });
                return;
            }

            // Once any async tasks are done, we can perform the final render
            // We also send the redux store state, so the client can continue execution where the server left off
            params.domainTasks.then(() => {
                resolve({
                    html: renderToString(app),
                    globals: { initialReduxState: store.getState() }
                });
            }, reject); // Also propagate any errors back into the host application
        });
    });  

这是布局文件:

    import * as React from 'react';
import {RouteComponentProps} from 'react-router-dom';
import {withRouter} from 'react-router';
import {connect} from 'react-redux';
import * as actions from '../Features/LanguageChanger/actions/languageChanger';
import LayoutProps from '../propertyTypes/LayoutProps';
import {ApplicationState} from '../store/initialState';
import Navbar from '../Features/Navbar/Navbar';
import NavbarContainer from '../containers/NavbarContainer';

class Layout extends React.Component<LayoutProps, {}> {
    public render() {
        return <div className='main-container'>
                <NavbarContainer {...this.props}/>
                {this.props.children}
        </div>;
    }
}

export default withRouter(connect(
    (state: ApplicationState) => state.language,
    actions.actionCreators,
)(Layout));

还有路由文件:

 import * as React from 'react';
import {Route} from 'react-router-dom';
import {Switch} from 'react-router';
import {Redirect} from 'react-router';
import Layout from './components/Layout';
import Home from './Features/Home/Home';
import About from './Features/About/About';
import ProductInfo from './Features/ProductInfo/ProductInfo';
import Questions from './Features/Questions/Questions';
import Shop from './Features/Shop/Shop';
import Contacts from './Features/Contacts/Contacts';

export const routes =
    <Layout>
            <Switch>
                <Route path='/:language/about' component={About}/>
                <Route path='/:language/product-info' component={ProductInfo}/>
                <Route path='/:language/questions' component={Questions}/>
                <Route path='/:language/shop' component={Shop}/>
                <Route path='/:language/contact-us' component={Contacts}/>
                <Route path='/:language' component={Home}/>
                <Route path='/' component={() => (<Redirect to={'/en'}/>)}/>
            </Switch>
    </Layout>;

我需要匹配对象,因为我需要根据 URL 中的语言发出请求。我的 languageChanger 组件位于导航栏中。我对这个问题的解决方案是从 URL 中手动提取我需要的东西。有人遇到过同样的问题吗?

【问题讨论】:

    标签: reactjs typescript redux react-router


    【解决方案1】:

    要在组件中获取正确的路由道具,您应该将 withRouter 包装为 connect

    export default connect(/* ... */)(withRouter(Layout);
    

    如果位置更改并且未被connect 阻止,它将更新您的组件。 (您可以在 React Router documentation 中阅读更多信息

    要从路径中获取占位符:langauge,您必须将组件包装在具有path 属性模式的路由组件中。

    Layout 组件内部:

    /* ... */
    return (
        <div className='main-container'>
            <Router path="/:language" render={({ match ) => (
                <NavbarContainer {...this.props} language={match.params.language} />
            )} />
            {this.props.children}
        </div>;
    );
    /* ... */
    

    这样导航栏只有在匹配路径模式时才会被渲染。您可以将render 更改为children,但是您必须处理可能为空的匹配项。 React Router 有一个很好的 documentation 关于 Route 的可能性。

    无论如何,您必须确保match.params.language 的值是真实的语言代码,因为有人可以在地址栏中输入路径/foo,您的语言将是foo

    【讨论】:

    • 感谢您的回答。我以前试过,但结果是一样的。我有匹配对象,但 match.params 是空的。它必须是 {language: en}。
    • 好的,这是有道理的,因为withRouter 内部的路由组件不知道正确的 url。我会更新我的答案
    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 1970-01-01
    • 2022-07-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    相关资源
    最近更新 更多