ReactでHoCsの代わりとしてFunction as Child Componentsを利用して型もつける ref: https://qiita.com/terrierscript/items/03df4d522cf9cf431021
// ネストしまくるのを回避したいので、Containerのとこだけ切り出して子に渡す
const MixedContainer = ({ children }) => {
return (
<AgeStateContainer>
{ageContainerProps => (
<BodyStateContainer>
{bodyStateContainerProps => {
return children({
...ageContainerProps,
...bodyStateContainerProps
});
}}
</BodyStateContainer>
)}
</AgeStateContainer>
);
};
class App extends Component {
render() {
return (
<div className="App">
<h1>Health tool</h1>
<MixedContainer>
{({ isYoung, handleWeight, handleHeight, height, weigth }) => (
<div>
{/* 今までと同じなので入力らへんは省略 */}
<div>
{isYoung ? (
<ForYoungHealthResult height={height}, weight={weigth}/>
) : (
<ForAdultHealthResult height={height}, weight={weigth}/>
)}
</div>
</div>
)}
</MixedContainer>
</div>
);
}
}
class AgeStateContainer extends Component {
state = {
age: 20
};
isYoung() {
return this.state.age < 16;
}
handleChange = e => {
this.setState({ age: e.target.value });
};
render() {
return (
<div>
Age <input value={this.state.age} onChange={this.handleChange} />
{/*子に返すのはisYoungだけ*/}
{this.props.children({ isYoung: this.isYoung() })}
</div>
);
}
}
class App extends Component<{},{}> {
render() {
return (
<BodyStateContainer>
{params => (
<div className="App">
<h1>Health tool</h1>
<div>
<h2>Input</h2>
<InputForm {...params} />
</div>
</div>
)}
</BodyStateContainer>
);
}
}
// spread operatorで受け取るのでChildrenPropsをそのまま流用
const InputForm: React.SFC<BodyChildrenProps> = ({
weight,
height,
handleWeight,
handleHeight
}) => {
return (
...
);
};
type BodyState = {
weight: number;
height: number;
};
type Handlers = {
handleHeight: React.ChangeEventHandler;
handleWeight: React.ChangeEventHandler;
// もうちょっと雑にするなら (e: any) => void;
};
type BodyChildrenProps = Handlers & BodyState;
type BodyStateProps = {
children: (props: BodyChildrenProps) => React.ReactNode;
// もうちょっと雑にするなら Function とか (props: any) => ReactNode とか
};
class BodyStateContainer extends Component<BodyStateProps, BodyState> {
:
render() {
return this.props.children({
:
:
})
}
}
class App extends Component {
render() {
return (
<BodyStateContainer>
{({ handleWeight, handleHeight, ...rest }) => (
<div className="App">
<h1>Health tool</h1>
<div>
<h2>Input</h2>
<InputForm
{...rest}
onChangeHeight={handleHeight}
onChangeWeight={handleWeight}
/>
</div>
<div>
<h2>BMI Result</h2>
<BMIResult {...rest} />
</div>
</div>
)}
</BodyStateContainer>
);
}
}
//InputFormとBMIResultは上記と同じなので省略する
class BodyStateContainer extends Component {
state = {
weight: 0,
height: 0
};
handleWeight = e => {
this.setState({
weight: e.target.value
});
};
handleHeight = e => {
this.setState({
height: e.target.value
});
};
render() {
return this.props.children({
...this.state,
handleHeight: this.handleHeight,
handleWeight: this.handleWeight
});
}
}
const InputForm = ({ weight, height, onChangeWeight, onChangeHeight }) => {
return (
<div>
<div>
Height:
<input value={height} type="number" onChange={onChangeHeight} />
cm
</div>,
<div>
Weight:
<input value={weight} type="number" onChange={onChangeWeight} />
kg
</div>
</div>
);
};
class BMIResult extends Component {
compute() {
const { weight, height } = this.props;
const meterHeight = height / 100;
return weight / (meterHeight * meterHeight);
}
render() {
const result = this.compute();
return <div>BMI: {result}</div>;
}
}
class App extends Component {
state = {
weight: 0,
height: 0
};
handleWeight = e => {
this.setState({
weight: e.target.value
});
};
handleHeight = e => {
this.setState({
height: e.target.value
});
};
render() {
return (
<div className="App">
<h1>BMI tool</h1>
<div>
<h2>Input</h2>
<InputForm
{...this.state}
onChangeHeight={this.handleHeight}
onChangeWeight={this.handleWeight}
/>
</div>
<div>
<h2>Result</h2>
<BMIResult {...this.state} />
</div>
</div>
);
}
}
class App extends Component {
state = {
weight: 0,
height: 0
};
computeBMI(weight, height) {
const meterHeight = height / 100;
return weight / (meterHeight * meterHeight);
}
handleWeight = e => {
this.setState({
weight: e.target.value
});
};
handleHeight = e => {
this.setState({
height: e.target.value
});
};
render() {
const { weight, height } = this.state;
const bmi = this.computeBMI(weight, height);
return (
<div className="App">
<h1>BMI tool</h1>
<div>
<h2>Input</h2>
<div>
Height:
<input value={height} type="number" onChange={this.handleHeight} />
cm
</div>
<div>
Weight:
<input value={weight} type="number" onChange={this.handleWeight} />
kg
</div>
</div>
<div>
<h2>Result</h2>
<div>BMI: {bmi}</div>
</div>
</div>
);
}
}
const ForAdultHealthResult = rest => {
return (
<div>
<h2>BMI Result</h2>
<ComputeBMI {...rest}>
{({ result }) => {
<div>Your BMI: {result}</div>;
}}
</ComputeBMI>
</div>
);
};
const ComputeBMI = ({ weight, height, children }) => {
const meterHeight = height / 100;
const result = weight / (meterHeight * meterHeight);
return children({ result });
};
type AgeContainerChildrenProps = {
isYoung: boolean;
};
type AgeContainerProps = {
children: (props: AgeContainerChildProps) => React.ReactNode;
};
type MixedChildrenProps = BodyChildrenProps & AgeContainerChildrenProps;
type MixedContaienrProps = {
children: (props: MixedChildrenProps) => React.ReactNode;
};
const WithStaticValue = ({ children }) => {
return children({ value: 1234 })
}
const MyItem = ({ value }) => {
return <div>{value}</div>
}
const WrappedMyItem = () => {
return (
<WithStaticValue>
{({ value }) => (
<MyItem value={value} />
)}
</WithStaticValue>
)
};
const withStaticValue = (WrappedComponent) => {
class HOC extends React.Component {
render() {
return (
<WrappedComponent
value={1234}
/>
);
}
}
return HOC;
};
const MyItem = ({value}) => {
return <div>{value}</div>
}
const WrappedMyItem = withStaticValue(MyItem)