我有一个应用程序,其中许多页面具有相似的外部功能,但内部功能不同。外部功能需要状态,例如,每个页面都需要有一个可以在页面上更改的日期,而不会影响其他页面上的各个日期。
我认为最好的重构方法是使用装饰器(HOC或渲染道具)。我曾去过渲染道具,但是我对这种模式还很陌生,我正在努力实现它。
这里是布局-我刚刚使用一个简单的计数器来说明我在创建具有单独状态的单独装饰组件时遇到的问题。
我有一个外部组件,其中包含React Router逻辑:
return (
<PageLayout>
<Grid item xs={12}>
<Grid container spacing={2}>
<Switch>
<Route
exact
path="/section1/overview"
render={() => (
<>
{<PageWrapper>{pageProperties => <Section1Test properties={pageProperties}/>}</PageWrapper>}
</>
)}
/>
<Route
exact
path="/section2/overview"
render={() => (
<>
{<PageWrapper>{pageProperties => <Section2Test properties={pageProperties}/>}</PageWrapper>}
</>
)}
/>
</Switch>
</Grid>
</Grid>
</PageLayout>
);
};
PageWrapper如下:
const PageWrapper = ({children}) => {
const [counter, setCounter] = useState(0)
const incrementCounter = () => {
setCounter(counter + 1)
}
return children({
counter,
incrementCounter
});
};
export default PageWrapper;
最后,每个部分如下所示:
const { counter, incrementCounter } = properties;
return (
<div>
{counter}
<button onClick={incrementCounter}>Click me</button>
</div>
因此,我要传递给props.children的properties对象包含counter和incrementCounter方法。
我的问题是,当我通过路由转到Section1Test并增加计数器时,它也使Section2Test的计数器增加。由于组件显然处于共享状态,因此无法达到使用装饰器的目的。
如果有人可以帮助我看看问题出在哪里,那将是很好的。
非常感谢。
答案 0 :(得分:0)
好吧,您正在PageWrapper内部的所有组件之间共享状态。解耦的一种方法是制作一个自定义钩子,以替代HOC:
const useCounter = () => {
const [counter, setCounter] = useState(0)
const incrementCounter = () => {
setCounter(counter + 1)
}
return {counter, incrementCounter};
}
// Inside component
const { counter, incrementCounter } = useCounter();
return (
<div>
{counter}
<button onClick={incrementCounter}>Click me</button>
</div>
);
答案 1 :(得分:0)
因此,我实际上只是出于娱乐目的做了一些工作,觉得我需要在React的各种方面变得更好。我对高阶组件进行了一些阅读,实际上我开发了一个高阶组件,将现有的组件馈入其中,它处理状态并向下发送{counter,crementCounter}作为道具,它还根据组件的名称维护状态您可以提供它(也可以根据需要通过可选变量或prop对其进行修改以维持状态)
签出:
HOC.js
:
import React from 'react';
import autoBind from 'react-autobind';
export default function HOC(WrappedComponent) {
const componentName = WrappedComponent.name;
return class extends React.Component {
constructor(props) {
super(props);
this.state = getState(componentName);
autoBind(this);
}
addOne() {
this.setState({counter: this.state.counter+1}, () => {
updateState(componentName, this.state);
})
}
render() {
return (
<div>
<p>this component is wrapped</p>
<WrappedComponent properties={{counter: this.state.counter, incrementCounter: this.addOne}}/>
</div>
)
}
};
}
const states = {};
function getState(name) {
if (name in states) return states[name];
return {counter: 0}
}
function updateState(name, newState) {
states[name] = newState
}
section1test.js
:
import React from 'react';
export default class Section1Test extends React.Component {
constructor(props) {
super(props);
}
render() {
const { counter, incrementCounter } = this.props.properties;
return (
<div>
<p>section1</p>
<input value={counter && counter.toFixed(2)}/> <br/>
<button onClick={incrementCounter}>Click me</button>
</div>
)
}
}
制作一个section2
,该{_1}大部分与之相似,但有所不同,然后在应该容纳第1部分和第2部分的组件中执行此操作:
// have this be ABOVE the class definition
const WrappedSection1 = HOC(Section1Test);
const WrappedSection2 = HOC(Section2Test);
...
// and then inside the render function
<Link to="/section3/overview">Three</Link><br/>
<Link to="/section4/overview">Four</Link><br/>
<Switch>
<Route
exact
path="/section3/overview"
render={() => (
<WrappedSection1 />
)}
/>
<Route
exact
path="/section4/overview"
render={() => (
<WrappedSection2 />
)}
/>
</Switch>
现在,第1部分和第2部分将具有唯一的计数器,当您返回第1部分或第2部分时,计数器状态将与您离开时的状态相同!