【问题标题】:React context value undefined in react-router反应路由器中未定义的反应上下文值
【发布时间】:2019-12-25 13:15:00
【问题描述】:

我正在尝试将 context api 与 react-router 一起使用。我有一个容器,它的 trackList prop 中有一个来自 mapStateToProps 的歌曲列表,我想使用上下文 api(没有 react-redux)将它传递给一个名为 TrackDetail 的无状态组件。正如您将在下面看到的,我同时使用了 useContext 钩子和渲染道具模式,但没有成功从上下文中获取该值。

这是我在 Routes 文件中的内容:

import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom";
import TrackList from "../../containers/TrackList/TrackList";
import TrackDetail from "../TrackDetail/TrackDetail";

class Routes extends Component {
  render() {
    return (
      <div className="Routes">
        <Router>
          <Switch>
            {this.props.children}
            <Route path="/tracks" component={TrackList} exact />
            <Route path="/tracks/:id" component={TrackDetail} exact/>
            <Route exact path="/" render={() => (<Redirect to="/tracks" />)} />
            <Route path="*" render={() => (<Redirect to="/tracks" />)} />
          </Switch>
        </Router>
      </div>
    );
  }
}

export default Routes;

这是我的 TrackList.jsx 容器(连接到 redux):

import React, { Component } from "react";
import { connect } from "react-redux";
import "./TrackList.scss";
import Spinner from "../../components/shared/Spinner/Spinner";

export const TrackListContext = React.createContext();

class TrackListProvider extends Component {

  state = {
    foo: "bar"
  }

  render() {
    return (
      <TrackListContext.Provider value={this.state}>
        {this.props.children}
      </TrackListContext.Provider>
    )
  }
};

export class UnConnectedTrackList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ""
    };
  }

  componentDidMount() {
    if (this.props.trackList.length === 0) {
      this.props.getTracks()
      .then(()=>this.props.changeSpinnerState());
      
    }
  }

  render() {
    if (this.props.spinnerState) return <Spinner />;
    return <TrackListProvider trackList={this.props.trackList}>
    <div>
    more content
    </div>
    </TrackListProvider>;
    
  }
}

const mapStateToProps = state => {
  const trackList = state.trackList.length !== 0 ? state.trackList : [];
  return {
    trackList,
  };
};
export default connect(
  mapStateToProps,
  null
)(UnConnectedTrackList);

这是我的 TrackDetail.jsx:

import React, { useContext } from "react";
import {TrackListContext} from '../../containers/TrackList/TrackList';
import "./TrackDetail.scss";

const TrackDetailCmp = (props) => {
  const value = useContext(TrackListContext);
  // value is undefined
  const currentTrackIndex = 0;
  const { trackList} = props;
  // trackList is undefined

  return (
    <div className="container">
      <h3></h3>
    </div>
  );
};

const withContext = (Component) => {
  return (props) => <TrackListContext.Consumer>
  {(value) => <Component {...props} trackList={value} />}
  </TrackListContext.Consumer>
};

const TrackDetail = withContext(TrackDetailCmp);
export default TrackDetail;

但我越来越不确定了。

可能是版本的问题。

这是我的 package.json:

{
  
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-redux": "^7.1.0",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.0.0",
    "redux": "^4.0.4",
    "redux-thunk": "^2.3.0"
  },
  "devDependencies": {
    "@types/jest": "^24.0.17",
    "@types/react": "^16.9.2",
    "enzyme": "^3.10.0",
    "enzyme-adapter-react-16": "^1.14.0",
    "node-sass": "^4.12.0"
  }
}

【问题讨论】:

    标签: reactjs react-router react-context


    【解决方案1】:

    首先要提到的是,react 路由器不应该影响这里的任何东西,因为它只是决定渲染哪些组件。

    为了使上下文起作用,上下文使用者必须是上下文提供者的子级。正如我在这里看到的,上下文提供者是“TrackList”组件,消费者是“Track”组件,但是这些是兄弟元素(并且当您使用带有反应路由器的开关时,只有一个路由会呈现,所以您甚至不能将它们呈现为兄弟姐妹)。

    我会尝试将整个路由器包装在上下文提供程序中,因为除了提供反应列表的上下文之外我看不到任何用法,我将删除它。你最终会得到类似这样的东西:

    class Routes extends Component {
      render() {
        return (
          <TrackListProvider value={{trackList: this.props.trackList}}>
              <div className="Routes">
                <Router>
                  <Switch>
                    {this.props.children}
                    <Route path="/tracks/:id" component={TrackDetail} exact/>
                    <Route exact path="/" render={() => (<Redirect to="/tracks" />)} />
                    <Route path="*" render={() => (<Redirect to="/tracks" />)} />
                  </Switch>
                </Router>
              </div>
            </TrackListProvider>
        );
      }
    }
    

    然后在您的曲目列表组件中,我将删除 HOC(您不想费心做两件事)。最后,上下文给出的结果是值,我的意思是将提供者的值设置为value={{trackList: this.props.trackList}},因此消费者将收到完整的对象。结果应如下所示:

    function TrackDetail(props) {
      const { trackList } = useContext(TrackListContext);
    
      return (
        <div className="container">
          <h3>{trackList}</h3>
        </div>
      );
    };
    
    export default TrackDetail;
    

    【讨论】:

    • 开头没有trackList。它应该被包装在 redux 中,或者拥有状态本身并在那些兄弟姐妹中使用该上下文。但是,我在一些线程中看到他们不推荐 1 个主要上下文。
    • 我明白你的意思,我将使用 react-redux 并仅在嵌套子级上使用上下文
    • 是的,我不知道曲目列表来自哪里,也没有 redux 经验,所以就让它保持打开状态哈哈。我还没有听说过任何与主要上下文相关的事情
    猜你喜欢
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多