0

I'm a beginner in React and stuck with some problem. I have two components. one is parent which passes its state to child component. the other is child which receives passed state and use it as props. What I wanted to do is when I click the list element in parent component(Detailsrch), its state changes and affect to child component(Btnfunctionlocal) so it will re render. This is for selecting address from parent component, and detailed address from child component.

I tried to use this.forceupdate and getderivedstatefromprops but It seems just make the work more difficult. And I tried to put parent's state into child's state. I used console.log to see what is happening, but console.log prints correct value but value in the browser doesn't change from the first one that selected from parent component.




Here is parent component

import React, { Component } from 'react'; import { Jumbotron } from 'react-bootstrap'; import { Btnfunctionlocal } from './Locals/Btnfunction'; import './Detailsrch.css'; class Detailsrch extends Component { constructor(props) { super(props); this.state = { local: '', localdetail: '' } this.whichlocal = (e) => { console.dir(e.target); // Here is the code that changes parent component's state this.setState({ local: e.target.innerText }) // From here to let selector = document.getElementsByClassName('locals'); let i = 0; while (selector[i]) { selector[i].classList.remove('localclick'); i++; } if (e.target.className === 'localtext') { e.target.parentElement.classList.add('localclick'); } else { e.target.classList.add('localclick'); } // here is for change css after click } } render() { console.log(this.state); return ( <Jumbotron className='searchjumbo'> <p>Address</p> <ul> {['All', 'Seoul', 'Incheon', 'Busan'].map((value, index) => { return <li className='locals' onClick={this.whichlocal} name={'localdiv' + index} key={index}><span className='localtext' name={'localdiv' + index}>{value}</span></li> })} </ul> {this.state.local?<Btnfunctionlocal local={this.state.local} />:''} <hr className='firsthr' /> </Jumbotron> ); } }; export default Detailsrch; 



Here is child component

export class Btnfunctionlocal extends Component { constructor(props){ super(props) const local = { Seoul: ['강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구'], Incheon: ['강화군', '계양구', '미추홀구', '남동구', '동구', '부평구', '서구', '연수구', '옹진구', '중구'], Busan: ['강서구', '금정구', '기장군', '남구', '동구', '동래구', '부산진구', '북구', '사상구', '사하구', '서구', '수영구', '연제구', '영도구', '중구', '해운대구'] } this.locallist = local[this.props.local].slice(); } render(){ return( <div className='localdiv'> {this.locallist.map((value, index)=>{ return <div className={"searchselector"} key={index}>{value}</div> })} </div> ) } } 

I also tried this

export class Btnfunctionlocal extends Component { constructor(props) { super(props) const local = { Seoul: ['강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구'], Incheon: ['강화군', '계양구', '미추홀구', '남동구', '동구', '부평구', '서구', '연수구', '옹진구', '중구'], Busan: ['강서구', '금정구', '기장군', '남구', '동구', '동래구', '부산진구', '북구', '사상구', '사하구', '서구', '수영구', '연제구', '영도구', '중구', '해운대구'] } this.state = { local: [] } } componentDidMount() { this.setState({ local: local[this.props.local].slice() }) } render() { return ( <div className='localdiv'> {this.state.local.slice().map((value, index) => { return <div className={"searchselector"} key={index}>{value}</div> })} </div> ) } } 



I expect when I click parent's list element, the child's elements will change, but except the first click, nothing changes.

8
  • 1
    In react component re-renders whenever there is a change in a state. As you update your state in the parent component it will re render but in child you are storing this in this.locallist or state local, which is not the right way. Use local[this.props.local].slice() or prop value straight inside render instead storing it. In case you want to still store in a state, store but after that use componentWillReceiveProps if react version is lower than 16.8 to compare nextProps and previous props value. If they are different you can update state again. Commented Aug 4, 2019 at 11:17
  • componentWillReceiveProps is deprecated and componentDidUpdate should be used instead. Commented Aug 4, 2019 at 11:23
  • @YogeshDevgun thanks so much. Here's one question. Doesn't component automatically re render itself if props has changed? Or Should I use componentDidUpdate to tell component the changes? Commented Aug 4, 2019 at 11:53
  • @momo1108: No component doesn't re render on props change straightway. It will re render on state change always. Commented Aug 4, 2019 at 12:06
  • @Domino987 Yes its deprecated, depending on react version installed componentDidUpdate or componentWillReceiveProps can be used to detect any change in props. Commented Aug 4, 2019 at 12:06

1 Answer 1

1

So there are several things, which I would change. But first to answer your question: You copy the prop into your local state on mount in your child component, but you don't update the state, if the parent props change. There is a way to fix that using componentDidUpdate:

componentDidUpdate(prevProps) { if(prevProps.local !== this.props.local) { this.setState({ local: local[this.props.local].slice() }) } } 

This will listen to prop changes and update your local state, which you access during your render method accordingly. Alternatively, you could directly use the parent prop during your render method and remove both componentDidUpdate and componentDidMount, like this:

render() { return ( <div className='localdiv'> {local[this.props.local].slice().map((value, index) => { return <div className={"searchselector"} key={index}>{value}</div> })} </div> ) } 

Additionally, I would not use document.getElementsByClassName at all and handle it with a ternary operation and js in css styles. Lastly, I would access the value of your change with e.target.value, because innerText might be different than value.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.