简体   繁体   English

回调渲染会丢失子级react-router和redux的路由器上下文

[英]callback render loses router context for child react-router & redux

I've been developing an idea but am getting stuck on something unusual (my brain hurts on react-router). 我一直在提出一个主意,但被一些不寻常的事情困住了(我的大脑在反应路由器上受伤了)。

I am trying to dynamically render a list of items using .map from a returned object (of multiple similar objects) and appending them to the render(){return(<div />)} . 我正在尝试使用.map从返回的对象(多个相似对象的对象)中动态呈现项目列表,并将其附加到render(){return(<div />)}

I just dont know another way than call a function then .map the result for this callback. 我只是不知道另一种方法,除了调用函数然后.map此回调的结果。

I think that the way I'm doing this means the rendered items lose context. 我认为我这样做的方式意味着渲染的项目失去了上下文。 The react-router <Link /> will function as expected in the normal flow (placed inside the render(){return(<div />)} ) but not when the item is created from outside of the render. react-router <Link />将在正常流程中正常运行(放置在render(){return(<div />)} ),但从渲染外部创建项目时则不会。 I have posted the error below the code. 我已将错误发布到代码下方。

I have read Many different ways of getting around this using context and location/history and withRouter. 我已经阅读了许多使用上下文和位置/历史以及withRouter解决此问题的方法。 Frankly I'm lost. 坦白说,我迷路了。

I would appreciate if someone could look at my example below and guide me in the right direction. 如果有人可以看下面的示例并指导我正确的方向,我将不胜感激。

A few notes: - main focus appears to be in mystuff - i have many unnecessary imports i know - stripped down for clarity, i would get lost otherwise 一些注意事项:-主要焦点似乎在神秘之中-我知道许多不必要的进口-为清楚起见而精简,否则我会迷路

index 指数

import _ from 'lodash';
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { store, history } from './store';

import Main from './Main';
import { routyr } from './Menu';

// remaining paths in Menu.js (routyr) for menu visibility
const router = (
  <Provider store={store}>
    <Router history={history}>
      <Route path="/" component={Main}>
        {routyr}
      </Route>
    </Router>
  </Provider>
)

render (router, document.getElementById('app'));

Main 主要

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from './actionCreators';

import App from './app';

function mapStateToProps(state){
  return{
    info: state.info,
    myProfile: state.myProfile
  }
}

function mapDispatchToProps(dispatch){
  return { actions: bindActionCreators(actionCreators, dispatch) }
}

const Main = connect(mapStateToProps, mapDispatchToProps)(App);
export default Main;

routyr 鲁特尔

import React from 'react';
import { Link } from 'react-router';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';

import { store, history } from './store';

//pages
import App from './app';
import Landing from './Landing';
import Me from './mystuff';
import ViewStuff from './viewStuff';

//Routes for index.js
export const routyr = (
  <span>
    <IndexRoute component={Landing} />
    <Route path="/myStuff" component={Me} />
    <Route path="/viewStuff" component={ViewStuff} />
  </span>
)

//Menu types
//loaded by app.js
export const menuLoggedIn = (
  <div className="MainMenu">
    <Link to='/' className="buttonA green">Home</Link>
    <Link to='myStuff' className="buttonA green">My Stuff</Link>
  </div>
);
export const menuLoggedOut = (
  <div className="MainMenu">
    <Link to='/login' className="buttonA green">Login</Link>
  </div>
);

app 应用程式

import React from 'react';
import _ from 'lodash';
import { Link } from 'react-router';
import auth from './auth';
import Landing from './Landing';
import Header from './Header';
import { menuLoggedIn, menuLoggedOut } from './Menu';

export default class App extends React.Component {
  constructor(){
    super();
    this.state={
      auth: auth.loggedIn(),
      menu: null
    };
  }

  componentWillMount(){
    if (this.state.auth==true) {
      this.setState({
        menu: menuLoggedIn
      })
    }else{
      this.setState({
        menu: menuLoggedOut
      });
    }
  }

  render(){
    return (
      <div>
        <Header />
        {this.state.menu}<br />
        <div id="view">
          {React.cloneElement(this.props.children, this.props)}
        </div>
      </div>
    );
  }
};

mystuff 我的东西

import React, { PropTypes } from 'react';
import { render } from 'react-dom';
import { Link } from 'react-router';
import { withRouter } from 'react-router';
import { Provider } from 'react-redux';

import * from './whacks';

export default class Me extends React.Component{
  constructor(){
    super();
  }
  componentDidMount() {

    function listThem(oio){
      oio.map(function(ducks){
        render(

          <div className="ListItem">
            <Link to="/viewStuff"> _BROKEN_ View Stuff</Link>
            <div className="listLabel">{ducks.type}</div>
            <h3>{ducks.description.title}</h3>
            {ducks.description.long}
          </div>, document.getElementById('fishes').appendChild(document.createElement('div'))

        );
      });
    }

    var some = new Whacks();

    some.thing(more, (close, open) => {

      if(close){
        console.log(close));
      } else {
        doIt(open);
      }

    });
  }

  render(){
    return(
      <div>
        <Link to="viewStuff"> _WORKING_ View Stuff</Link>
        <div id="fishes">
        </div>
      </div>
    )
  }
}

store 商店

import { createStore, compose } from 'redux';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';

/*-------ROOT REDUCER---------*/
/*-------DEFAULT STATES---------*/
/*-------CREATE STORE---------*/
/*-------INTEGRATE HISTORY---------*/

import me from './reducers/obj';
import myProfile from './reducers/myProfile';

const rootReducer = combineReducers(
  {
    routing: routerReducer,
    me,
    myProfile
  }
);

//TEMP remove harcoded var
const uuidSet = "fa78d964";
export const defaultState = {
  uuid: uuidSet,
};

export const store = createStore(rootReducer, defaultState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export const history = syncHistoryWithStore(browserHistory, store);

actionCreators actionCreators

export function me (obj){
  return {
    type: "ADD_OBJECTLIST",
    obj
  }
}

export function myProfile (dump){
  return {
    type: "MY_DATA",
    dump
  }
}

from package.json 来自package.json

"react-redux": "^5.0.2",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.7",
"redux": "^3.6.0",

error 错误

Uncaught Error: s rendered outside of a router context cannot navigate. 未捕获的错误:在路由器上下文之外呈现的不能导航。

@UG, I have tried the following in mystuff: @UG,我在Mystuff中尝试了以下操作:

constructor(){
  super();
  this.state={
    oio: {}
  };
}

and

some.thing(more, (close, open) => {

      if(close){
        console.log(close));
      } else {
        this.setState({
          oio: open
        });
      }

});

and

render(){
  let flat = this.state.oio;
  flat.map(function(ducks){
    return (
      <div className="ListItem">
        <Link to="/viewStuff">View Stuff</Link>
        <div className="listLabel">{ducks.type}</div>
        <h3>{ducks.description.title}</h3>
        {ducks.description.long}
      </div>
    )
  })
}

and receive 并收到

Uncaught TypeError: flat.map is not a function at Me.render 未捕获的TypeError:flat.map不是Me.render的函数

I am not sure if I get your issue completely. 我不确定是否能完全解决您的问题。 But I think you want to use Link inside render() method of myStuff You can change that to following : 但我认为您想在myStuff render()方法内使用Link,可以将其更改为以下内容:

render(){
return(
  <div>
    <Link to="viewStuff"> _WORKING_ View Stuff</Link>
    <div id="fishes">
        {
            oio.map(function(ducks){
                return (
                    <div className="ListItem">
                        <Link to="/viewStuff"> _BROKEN_ View Stuff</Link>
                        <div className="listLabel">{ducks.type}</div>
                        <h3>{ducks.description.title}</h3>
                        {ducks.description.long}
                    </div>
                );
            }
    </div>
  </div>
    )
}

As per the comment from James, 根据詹姆斯的评论,

You should use react state to maintain oio object. 您应该使用react状态来维护oio对象。

constructor() {
 super(); 
 //init
 this.setState({oio : {}});
}

and update the state in async call, when state updates, component can be rerendered. 并在异步调用中更新状态,当状态更新时,可以重新呈现组件。

Huge thanks to UG_ for smacking me in the ear with state. 非常感谢UG_将状态打在我的耳朵上。 I have pulled in a component and created each components props from the callback objects. 我已经引入了一个组件,并从回调对象创建了每个组件的道具。 My Working solution is as follows in mystuff: 我的工作解决方案如下所示:

constructor(props){
  super(props);
  this.state={
    oio: []
  }
}
componentDidMount() {

  let listThem = (stuff) => {
    let ioi = [];
    stuff.forEach(function(dood, index, array) {
      let lame = <MyItem plop={dood} key={index} />;
      ioi.push(lame);
    });
    return (
      this.setState({
        oio: ioi
      })
    );
  }

  var some = new Whacks();
  some.thing(more, (close, open) => {
    if(close){
      console.log(close));
    } else {
      listThem(open);
    }
  });

}
render(){
  return(
    <div>
      {this.state.oio}
    </div>
  )
}

Which renders a new copy of the MyItem component with props from each returned object. 它将使用来自每个返回对象的道具来呈现MyItem组件的新副本。 So now my returned items contain context! 所以现在我退回的物品包含上下文!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM