我将如何重组我的React App,以便可以将状态传递给另一条路线上的组件

时间:2018-10-05 19:56:17

标签: javascript reactjs

我将三个组件路由到不同的路径。我想重组App,以便可以通过道具将状态从我的SubmitProject组件传递到Portfolio组件,我仍然希望它们具有单独的路径,即; /portfolio/SubmitProject我打算打开两个浏览器窗口,以测试当我在SubmitProject上提交表单时,它将显示在Portfolio上,然后我将使用firebase进行持久化我的状态到数据库。

我是否需要将状态设为App.js之类的顶级组件,然后在其中包含BrowserRouter?如果是这样,我如何重新创建从<SubmitProject/>-> <PortfolioForm/>-> <FormAdd/>

建立的连接

我期望的目标是,当我在FormAdd路线上时,从/submit组件提交表单时,它将通过{{1} 1}}路线。

建议使用状态管理器(例如context api或其他),但我想知道是否存在一种方法来重组我的App并能够从每个组件和路由的顶级组件传递状态分享。

这是我的相关代码

Portfolio

/Portfolio

components/Router.js //我的路由器应该在这里吗?

import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Portfolio from './Portfolio';
import SubmitProject from './SubmitProject';
import App from './App';

const Router = () => (
    <BrowserRouter>
        <Switch>
            <Route exact path="/" component={App}/>
            <Route exact path="/portfolio" component={Portfolio}/>
            <Route exact path="/submit" component={SubmitProject}/>
        </Switch>
    </BrowserRouter>
);

export default Router;

components/App.js

import React from 'react';

class App extends React.Component {

  render() {
      return <div>Test</div>
  }

}

export default App;

/components/SubmitProject.js

import React from 'react';
import PortfolioForm from './PortfolioForm';
import Section from './Section';

class SubmitProject extends React.Component {
    state = {
        sections:{}
    };
    addSection = section =>{
        const sections = {...this.state.sections};
        sections[`section${Date.now()}`] = section;
        this.setState({
            sections: sections
        });
    }
    render() {
        return(
            <React.Fragment>
                <h1>Submit Project</h1>
                <h2>Enter Project Data</h2>
                <ul className="section">
                    {Object.keys(this.state.sections).map(key => <Section key={key} details={this.state.sections[key]}/>)}
                </ul>
                <PortfolioForm addSection={this.addSection} />
            </React.Fragment>
        )
    }
}

export default SubmitProject;

/components/PortfolioForm.js

import React from 'react';
import FormAdd from './FormAdd';

class Portfolio extends React.Component {
    render() {
        return(
            <React.Fragment>
                <h1>Submit Form</h1>
                <FormAdd addSection={this.props.addSection}/>
            </React.Fragment>
        )
    }
}

export default Portfolio;

/components/FormAdd.js

import React from 'react';

class FormAdd extends React.Component {
    nameRef = React.createRef();

    createSection = (event) =>{
        event.preventDefault();
        const section = {
            name: this.nameRef.current.value
        };
        this.props.addSection(section);
    };  
    render() {
        return(
            <React.Fragment>
                <form onSubmit={this.createSection}>
                    <input type="text" ref={this.nameRef} name="name" placeholder="Name"/>
                    <button type="submit">+ Add Section</button>
                </form>
            </React.Fragment>
        )
    }
}

export default FormAdd;

更新的代码 我现在收到一条错误消息,提示/components/Portfolio.js

import React from 'react'; class Portfolio extends React.Component { //CAN I GET STATE FROM SubmitProject.js FILE IN HERE? By Restructuring my App Somehow. render() { return( <React.Fragment> <h1>Portfolio Page</h1> <h2>List of projects</h2> </React.Fragment> ) } } export default Portfolio;

FooContext is not defined

更新的代码V#2 components/App.js

import React from 'react';
import SubmitProject from './SubmitProject';
import {BrowserRouter, Route, Switch} from 'react-router-dom';

const FooContext = React.createContext();

class App extends React.Component {
  state = {
    sections:{}
  };
  addSection = section =>{
      const sections = {...this.state.sections};
      sections[`section${Date.now()}`] = section;
      this.setState({
          sections: sections
      });
  }

  render() {
      return (
        <FooContext.Provider value={this.state.sections}>
          <Router/>;
        </FooContext.Provider>
      )
  }

}

class Router extends React.PureComponent {
  render() {
    return 
    <BrowserRouter>
        <Switch>
            <Route exact path="/" component={Root} />      
        </Switch>
    </BrowserRouter>
  }
}

const Root = props => <FooContext.Consumer>{sections => <SubmitProject/> }</FooContext.Consumer>;

export default App;

App.js

import React from 'react';
import SubmitProject from './SubmitProject';
import Home from './Home';
import {BrowserRouter, Route, Switch} from 'react-router-dom';

const FooContext = React.createContext();

class App extends React.Component {
  state = {
    sections:{}
  };
  addSection = section =>{
      const sections = {...this.state.sections};
      sections[`section${Date.now()}`] = section;
      this.setState({
          sections: sections
      });
  }

  render() {
      return (
        <FooContext.Provider value={this.state.sections}>
          <Router/>;
        </FooContext.Provider>
      )
  }

}

class Router extends React.PureComponent {
  render() {
    return (
      <BrowserRouter>
        <Switch>
            <Route exact path="/" component={Home} /> 
            <Route exact path="/portfolio" component={Portfolio} />      
        </Switch>
      </BrowserRouter> 
    )

  }
}

const Portfolio = props => <FooContext.Consumer>{foo => <SubmitProject/>}</FooContext.Consumer>;

export default App;

1 个答案:

答案 0 :(得分:0)

  

建议使用状态管理器(例如context api或其他),但我想知道是否存在一种方法来重组我的App并能够从每个组件和路由的顶级组件传递状态分享。

这种方法存在问题。

考虑到App维护应用程序状态,有必要将它作为prop传递给<Router>,然后路由依赖于它的组件:

class App extends React.Component {
  state = { foo: true };

  render() {
      return <Router foo={this.state.foo}/>
  }
}

const Router = props => (
    const RootWithFoo = props => <Root foo={props.foo}/>;

    return <BrowserRouter>
        <Switch>
            <Route exact path="/" component={RootWithFoo} />
            ...
        </Switch>
    </BrowserRouter>
);

这限制了组件的结构;为了避免深入传递道具,应删除Router组件,并应直接在Route中呈现App

class App extends React.Component {
  state = { foo: true };

  render() {
    const RootWithFoo = props => <Root foo={this.state.foo}/>;

    return <BrowserRouter>
        <Switch>
            <Route exact path="/" component={RootWithFoo} />
            ...
        </Switch>
    </BrowserRouter>
  }
}

这是上下文API和状态管理库(例如Redux)解决的问题。它们允许为使用嵌套组件的组件提供全局状态。

另一个问题是性能。每次状态更新时,整个路由器将重新呈现。同样,上下文API和状态管理库可以解决此问题。正如context API manual所述:

  

作为提供商后代的所有消费者将在提供商的价值属性发生更改时重新呈现。从Provider到其后代的Consumer的传播不受shouldComponentUpdate方法的约束,因此即使祖先组件无法进行更新,也要对Consumer进行更新。

因此,如果上下文提供者的值更新,则无需重新渲染整个树。无论如何,上下文使用者将被重新渲染。由于默认情况下将重新渲染整个树,因此Provider child(ren)应该是纯组件,以避免不必要的重新渲染。这就是为什么最好将AppRouter组件分开的原因:

class App extends React.Component {
  state = { foo: true };

  render() {
    return <FooContext.Provider value={this.state.foo}>
      <Router/>;
    </FooContext.Provider>
  }
}

class Router extends React.PureComponent {
  render() {
    return <BrowserRouter>
        <Switch>
            <Route exact path="/" component={Root} />
            ...
        </Switch>
    </BrowserRouter>
  }
}

const Root = props => <FooContext.Consumer>{foo => ...}</FooContext.Consumer>;

更新全局状态后,仅重新渲染App和依赖于该状态的路由组件(Root等),而不渲染Router