简体   繁体   English

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

[英]How would I restructure my React App, so that I can pass state down to a component on a different route

I have three components routed to different paths. 我将三个组件路由到不同的路径。 I want to restructure my App so that I can pass state via props from my SubmitProject Component to my Portfolio Component I still want them to have separate paths ie; 我想重组我的应用程序,以便我可以通过props将状态从SubmitProject组件传递到我的Portfolio组件。我仍然希望它们具有单独的路径,即; /portfolio and /SubmitProject I plan to have two browserwindows open to test that when I submit a form on SubmitProject it will show up on Portfolio then I will be using firebase to persist my state to a database. /portfolio/SubmitProject我计划打开两个浏览器窗口以测试当我在SubmitProject上提交表单时它将显示在Portfolio然后我将使用SubmitProject将状态持久化到数据库中。

Do I need to have my state be at a top level Component like App.js and then have the BrowserRouter inside of that? 我是否需要将状态App.js类的顶级组件,然后在其中BrowserRouter If so how do I recreate the connections I have made from <SubmitProject/> -> <PortfolioForm/> -> <FormAdd/> 如果是这样,如何重新创建从<SubmitProject/> -> <PortfolioForm/> -> <FormAdd/>

My Desired Goal is that when I submit the form from the FormAdd Component when I am on the /submit Route that it will output via state on my Portfolio Component on the /Portfolio Route. 我期望的目标是,当我在/submit路线上从FormAdd组件提交表单时,它将通过/Portfolio路线上我的Portfolio组件上的状态输出。

It has been recommend to use a state manager like context api, or something else, but I want to know if there is a way to restructure my App and be able to pass state from a top level component that each component and route share. 建议使用状态管理器(如上下文api)或其他方法,但我想知道是否存在一种方法来重组我的App,并能够从每个组件和路由共享的顶级组件传递状态。

Here is my relevant code 这是我的相关代码

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 // Should My Router be in here? 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;

UPDATED CODE I am now getting an error that says FooContext is not defined 更新的代码,现在我得到一个错误,提示FooContext is not defined

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;

UPDATED CODE V#2 App.js 更新的代码V#2 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;

SubmitProject.js

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

class SubmitProject extends React.Component {

    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;

It has been recommend to use a state manager like context api, or something else, but I want to know if there is a way to restructure my App and be able to pass state from a top level component that each component and route share. 建议使用状态管理器(如上下文api)或其他方法,但我想知道是否存在一种方法来重组我的App,并能够从每个组件和路由共享的顶级组件传递状态。

There are problems with this approach. 这种方法存在问题。

Considering that App maintains application state, it's necessary to pass it to <Router> as a prop and then to route components that depend on it: 考虑到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>
);

This puts a restriction on component structure; 这限制了组件的结构。 in order to avoid deeply passed props, Router component should be removed, and Route should be rendered directly in App : 为了避免深入传递道具,应删除Router组件,并应直接在App呈现Route

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>
  }
}

This is a problem that context API and state management libraries (eg Redux) address. 这是上下文API和状态管理库(例如Redux)解决的问题。 They allow to provide global state for nested components where it's used. 它们允许为使用嵌套状态的组件提供全局状态。

Another problem is performance. 另一个问题是性能。 The whole router will be re-rendered on each state update. 每次状态更新时,整个路由器将重新呈现。 Again, context API and state management libraries address this. 同样,上下文API和状态管理库可以解决此问题。 As context API manual states: 上下文API手册所述:

All Consumers that are descendants of a Provider will re-render whenever the Provider's value prop changes. 只要提供商的价值支柱发生变化,作为提供商的后代的所有消费者都将重新渲染。 The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update. 从Provider到其后代Consumer的传播不受shouldComponentUpdate方法的限制,因此即使祖先组件退出了更新,也要对Consumer进行更新。

So if context provider value updates, it's unnecessary to re-render the whole tree. 因此,如果上下文提供者的值更新,则无需重新渲染整个树。 Context consumer will be re-rendered regardless of this. 无论如何,上下文使用者将被重新渲染。 Since the whole tree will be re-rendered by default, Provider child(ren) should be a pure component to avoid unnecessary re-renders. 由于默认情况下将重新渲染整个树,因此Provider child(ren)应该是纯组件,以避免不必要的重新渲染。 This is a reason why separated App and Router components may be preferable: 这就是为什么最好将分离的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>;

When global state is updated, only App and route components that depend on it ( Root , etc.) are re-rendered but not Router . 更新全局状态后,仅重新渲染依赖于该状态的App和路由组件( Root等),而不是Router

暂无
暂无

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

相关问题 如何将变量传递到子组件中,以便它们与React Router一起使用? - How can I pass down variables into child component so that they work with React Router? 如何将 state 从一条反应路线传递到另一条反应路线? - How can i pass a state from one react route to another react route? 如何删除事件侦听器,以便在卸载组件时不尝试更新状态? - How can I remove event Listener so it would not try to update state when component is unmounted? 调整屏幕大小时如何设置反应组件的 state? - How would I set the state of a react component when I resize my screen? 如何更新我的 React 组件 state? - How can I update my React component state? 如何将 state 传递到我的 react-router 路径? - How can I pass a state to my react-router path? 我如何在react中将路由参数值传递给我的标头组件和页面内容组件? - How do i pass a route parameter value to both my header component and page content component in react? 如何在 react 中重定向到另一个组件并传递我在前一个组件中设置的 state? - How can I redirect to another component in react and pass the state that I set in the previous component? 如何在将相同 ID 分组的地方重组数据? - How can I restructure the data where it would group the same IDs? 如何导出 {this.props.price * this.state.quantity} 的值,以便可以在不同的.js 文件中使用它 // 不同的反应组件? - How can I export the value of {this.props.price * this.state.quantity} so I could use it in a different .js file // a different react component?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM