用React.cloneElement调用父方法

时间:2018-11-06 06:10:16

标签: reactjs react-props

我尝试了这个https://medium.com/@markgituma/passing-data-to-props-children-in-react-5399baea0356,但对我不起作用。

/**
*  Router.js
*/

import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Layout from './components/Layout';
import AddExercise from './pages/AddExercise';

const Router = () => (
        <BrowserRouter>
            <Layout>
                <Switch>
                  <Route path="/add-exercise" component={AddExercise} />
                </Switch>
            </Layout>
        </BrowserRouter>
);

export default Router;

/**
*  Layout.js
*/

class Layout extends React.Component {
    render() {

        const children = React.Children.map(this.props.children, child => {
            return React.cloneElement(child, {
                testFunc: () => console.log('test function')
            });
        });
        return (
          <div>{ children }</div>
        )
    }
}

export default Layout;

/**
*  AddExercise.js
*/

const AddExercise = ({ testFunc }) => (
  <button onClick={testFunc}>Custom Btn</button>
);

export default AddExercise;

当我单击AddExercise.js中的按钮时,没有任何反应。可能是因为我正在尝试将它与react-router-dom一起使用?有解决办法吗?

3 个答案:

答案 0 :(得分:0)

您遇到的问题是,当您尝试使用testFunccloneElement传递给子级时,它作为对Switch Component(在您的情况下是子级或Layout Component)的支持而传递。并且switch组件对此道具没有任何作用,并且不会传递给AddExercise组件。

处理情况的最佳方法是避免使用cloneElement并直接在布局组件中使用路线

/**
*  Router.js
*/

import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Layout from './components/Layout';
import AddExercise from './pages/AddExercise';

const Router = () => (
        <BrowserRouter>
            <Route component={Layout} /> // This is needed so that Layout gets the Router props
        </BrowserRouter>
);

export default Router;

/**
*  Layout.js
*/

class Layout extends React.Component {
    render() {
        return (
          <div>
            <Switch>
              <Route path="/add-exercise" render={(props) => <AddExercise {...props} testFunc: () => console.log('test function')/>} />
            </Switch>
          </div>
        )
    }
}

export default Layout;

/**
*  AddExercise.js
*/

const AddExercise = ({ testFunc }) => (
  <button onClick={testFunc}>Custom Btn</button>
);

export default AddExercise;

其他不涉及在Layout中编写Routes的解决方案将包括创建另一个包含Routes之类的组件

  <BrowserRouter>
        <Layout>
            <ChildRoutes />
        </Layout>
    </BrowserRouter>

ChildRoutes.js

export const ChildRoutes = ({ testFunc }) => {
    <Switch>
      <Route path="/add-exercise" render={(props) => <AddExercise {...props}  testFunc={testFunc}/>} />
    </Switch>
}

答案 1 :(得分:0)

如您共享的link中所述,我们应该将布局组合到一个组件中。您可以尝试创建一个新组件,例如“ FullLayout”,其中同时包含Layout和AddExercise组件。

/**
FullLayout.js
*/

import React from 'react'
import Layout from './Layout'
import AddExercise from '../pages/AddExercise'

class FullLayout extends React.Component {
    render = () => {
        return (
        <Layout>
            <AddExercise></AddExercise>
            <AddExercise></AddExercise>
        </Layout>
        );        
    }
}

export default FullLayout

然后,您只需要在Router.js中将组件修改为FullLayout

<Route path="/add-exercise" component={FullLayout} />

这应该可以解决问题

答案 2 :(得分:0)

谢谢大家!这样做很不错:

<Switch>
  <Route path="/add-exercise" render={(props) => <AddExercise {...props}  testFunc={testFunc}/>} />
</Switch>

但是看起来有点脏,因为我有多个路线(更多)。因此,我尝试使用Context API,并且非常完美。我不必重组我的组件。

AppContext.js

const AppContext = React.createContext({});

export const AppProvider = AppContext.Provider;
export const AppConsumer = AppContext.Consumer;

Layout.js

import { AppProvider } from './AppContext';

class Layout extends React.Component {

    testFunc = () => console.log('test function');

    render() {
        return (
            <AppProvider value={this.testFunc}> // just wrap it with the AppProvider here and send the method
                    {React.cloneElement(this.props.children, this.props)}
            </AppProvider>
        )
    }
}

AddExercise.js

import {AppConsumer} from './AppContext';

class AddExercise extends React.Component {
    render() {
        return(
            <button onClick={this.props.testFunc}>Custom Btn</button>
        );
    }    
}

export default props => ( // this is the weird part, but works
    <AppConsumer>
      {context => <AddExercise {...props} testFunc={context} />}
    </AppConsumer>
);