4razmus 4razmus - 1 month ago 19
React JSX Question

React router pass specific params to child compents

I have a perplexing problem that I cant figure out. I am new to react + flux + react router.

I have the following routes:

<Router history={browserHistory} >
<Route path="/" component={App}>
<IndexRoute component={Search}/>
<Route path="/login" component={Login} />
<Route name="client" path="/client(/:clientid)" component={Client}/>
{/* <Route name="tel" path="/tel/:telid" component={Tel}/>*/}
</Route>
</Router>


I want to be able to pass specific params to specific routes. I know i can pass all params to all routes using:

{this.props.children && React.cloneElement(this.props.children, {...})}


however I dont want to allow sub components access to other component states. Ideally I'd like to pass the loginState to the login component, the searchState to the search component and the clientState to the client component. with the above method the client component can access all component states.

I currently have this workaround but it feels dirty and not very future proof:

var React = require('react');

var AppStore = require('../stores/AppStore');

var browserHistory = require('react-router').browserHistory;

// Flux cart view
var App = React.createClass({

getInitialState() {
return AppStore.getState();
},

componentWillMount() {
if (!this.state.auth.loggedIn) {
browserHistory.push('/login');
}
},
// Add change listeners to stores
componentDidMount() {
AppStore.addChangeListener(this._onChange);
},

// Remove change listers from stores
componentWillUnmount() {
AppStore.removeChangeListener(this._onChange);
},

// Method to setState based upon Store changes
_onChange(){
this.setState(AppStore.getState());
},

// Render cart view
render() {
console.log(this.props.children);
var name = this.props.children.type.displayName;
var p;
switch(name) {
case 'client':
p = {clientState: 'test'}
break;
case 'login':
p = {auth: this.state.auth}
break;
}
return (
<div>
{this.props.children && React.cloneElement(this.props.children, p)}
</div>
);
}

});

module.exports = App;


Any thoughts on how to achieve this correctly? I have looked at a lot of google results but most harken back to the old versions of react and react-router.

Answer

you can use named components

...     
<Route path="/login" components={{login:Login}} />
<Route path="/client(/:clientid)" components={{client:Client}}/>
...

And in App component

class App extends Component {
    constructor(props){
        super(props);
        this.setProps= this.setProps.bind(this);
    }
    setProps(comp, props){
        if(!comp) return null;
        return React.cloneElement(comp, props);
    }
    render(){
        const {client, login}=this.props;
        const {clientData, loginData}=this.state;

        return (<div>
            {this.setProps(client,clientData)}
            {this.setProps(login,loginData)}
        </div>);
    }
}

export default App;

it also usefull when on page more then one component per route