Reputation: 6197
I have two components, parent and child.
Parent:
class Parent extends React.Component {
constructor(){
super()
this.state = {
select: false
}
}
isSelect(){
this.setState({...this.state, select: !this.state.select})
}
render(){
const { Header, children } = this.props
return (
<>
<Header isSelect={this.isSelect} />
</>
)
}
}
Child:
class Child extends React.Component {
render(){
const {title, isSelect} = this.props
/*problem here! can not get isSelect*/
return(
<div>
<p>{title}</p>
<button onClick={this.isSlect}>choose</button>
/*problem here! isSelect*/
</div>
)
}
}
In use:
<Parent Header={<Child title='just for test' />} />
The components can be rendered but not for click event.
I want to set the isSlect
function automatically for Child component, cause it just call the Parent function and revert the Boolean value. so It's no meaning to pass it again in use.
The problem is How can I pass this isSelect
? It seems like isSelect={this.isSelect}
is overwritten from <Header isSelect={this.isSelect} />
.
Upvotes: 1
Views: 110
Reputation: 7492
You could change the behavior of Header
to be a function passing the isSelect
callback in its argument :
render() {
const { Header, children } = this.props
return Header(this.isSelect)
}
The component can now assign the received function however you want :
<Parent Header={clicked => <Child title='just for test' isSelect={clicked} />} />
Working live example :
class Parent extends React.Component {
constructor(props) {
super(props) //Don't forget to send the props in super
this.state = {
select: false
}
}
isSelect = () => { //Arrow function to avoid having to bind it
this.setState({ ...this.state, select: !this.state.select })
console.log('Selected')
}
render() {
const { Header, children } = this.props
return Header(this.isSelect)
}
}
class Child extends React.Component {
render() {
const { title, isSelect } = this.props
return (
<div>
<p>{title}</p>
<button onClick={isSelect}>choose</button> {/* Careful, you do not need to add 'this.' */}
</div>
)
}
}
ReactDOM.render(<Parent Header={clicked => <Child title='just for test' isSelect={clicked} />} />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id='root'>
Upvotes: 1
Reputation: 3234
JS class methods are not bound by default, so if you pass isSelect
like you did in your code, this
will be undefined (and this.setState
won't work).
You can either pass an anonymous arrrow function calling isSelect
to the child compoment:
<Child isSelect={() => this.isSelect()} />
or just bind it in the Parent
contructor:
this.isSelect = this.isSelect.bind(this)
or use public class fields (if you use Babel or some other transpiler, they are still experimental):
class Parent extends React.Component {
…
isSelect = () => {
this.setState(…)
}
}
Working example:
class Parent extends React.Component {
constructor(){
super()
this.state = {
select: false
}
this.isSelect = this.isSelect.bind(this)
}
isSelect(){
this.setState({...this.state, select: !this.state.select})
}
render(){
const { Header, children } = this.props
return (
<div>
<Child isSelect={this.isSelect} />
<Child isSelect={() => this.isSelect()} /> {/* this one will work even without .bind in the constructor */}
<code>{JSON.stringify(this.state, null, 2)}</code>
</div>
)
}
}
class Child extends React.Component {
render(){
return(
<div>
<button onClick={this.props.isSelect}>choose</button>
</div>
)
}
}
ReactDOM.render(
(<Parent/>),
document.querySelector("#root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 0