Hello i'm new on react and have to do a Tutorial where i have to change the state of the Child Component with a button onClick function. currently i'm use a button in my Parent component to do it and it works but now i have to use call this button in other child components and not directly in the Parent to restart my Tutorial.
but i dont know how i can do it. ill happy about every suggestion.
class Child extends React.Component {
constructor (props) {
super(props)
this.state = {
run: this.props.run,
stepIndex: this.props.stepIndex
}
}
componentWillReceiveProps (props) {
this.setState({ run: props.run, stepIndex: props.stepIndex })
}
callback = (tour) => {
const { action, index, type } = tour
// if you delete action === 'skip', the Tutorial will not start on other pages after skipped once.
if (action === 'close' || action === 'skip' || type === 'tour:end') {
this.setState({ run: false })
} else if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
this.setState({ stepIndex: index + (action === ACTIONS.PREV ? -1 : 1) })
}
}
render () {
let { run, stepIndex, steps } = this.state
if (this.props.location.pathname === '/') {
steps = []
run = false
} else if (this.props.location.pathname === '/matches/' || this.props.location.pathname.length === '/matches') {
steps = matchSiteSteps
} else if (this.props.location.pathname.startsWith('/matches/') && this.props.location.pathname.endsWith('/edit/')) {
steps = matchEditorSteps
} else if (this.props.location.pathname.startsWith('/matches/') && this.props.location.pathname.includes('sequence')) {
steps = matchSequenceSteps
} else if (this.props.location.pathname.startsWith('/matches/') && this.props.location.pathname.match('\\d+')) {
steps = matchSteps
}
return (
<>
<Joyride
callback={this.callback}
run={run}
stepIndex={stepIndex}
steps={steps}
continuous
disableOverlayClose
spotlightClicks
showSkipButton
locale={{
back: <span>Zurück</span>,
last: (<span>Beenden</span>),
next: (<span>Weiter</span>)
}}
/>
</>
)
}
}
class Parent extends Component {
constructor (props) {
super(props)
this.handler = this.handler.bind(this)
this.state = {
run: true,
stepIndex: 0,
}
}
handler () {
this.setState({ run: true, stepIndex: 0 })
}
render () {
return (
//some other stuff
<RestartButton handler={this.handler} />
<Tutorial run={this.state.run} stepIndex={this.state.stepIndex} />
//some other stuff
)
}
}
class RestartButton extends React.Component {
render () {
return (
<button className='restartButton' onClick={() => this.props.handler()}>click</button>
)
}
}
You shouldn't store props in the child component state if state.run
and state.stepIndex
are always going to be the same as props.run
and props.stepIndex
. Instead you should just use those props directly when you need to use them in the render method. stepIndex={this.props.stepIndex}
will pass exactly the same values to the Joyride component as setting the child component's state.stepIndex
equal to props.stepIndex
and then passing stepIndex={this.state.stepIndex}
.
If you want to change the value of the parent component's state from a child component, you can pass the handler method (bound to the parent component) through as many layers of components as you want, or to as many different children as you want.
class Tutorial extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<>
<RestartButton handler={this.props.handler}/>
<Joyride
callback={this.callback}
run={this.props.run}
stepIndex = {this.props.stepIndex}
steps={steps}
continuous
disableOverlayClose
spotlightClicks
showSkipButton
locale={{
back: < span > Zurück < /span>,
last: ( < span > Beenden < /span>),
next: ( < span > Weiter < /span>)
}}
/>
</>
)
}
}
class Parent extends Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
this.state = {
run: true,
stepIndex: 0,
}
}
handler() {
this.setState({run: true, stepIndex: 0})
}
render() {
return (
<Tutorial
run={this.state.run}
stepIndex={this.state.stepIndex}
/>
)
}
}
class RestartButton extends React.Component {
render() {
return (
<button
className='restartButton'
onClick={() => this.props.handler()}
> click </button>
)
}
}
(Also, componentWillReceiveProps is deprecated and you should use componentDidUpdate instead, if you do need to do something on component update).