[英]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: 这就是为什么最好将分离的App
和Router
组件作为首选的原因:
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.