简体   繁体   中英

react child component not updating after parent state changed

I am trying to make a react messaging app where the channel page is composed a channel bar Channel.js (Parent Component) with a general and random channel and a message-list ChannelMessage.js (Child Component).

Currently, I can click on the channel bar and it changes the url and this.props.channelName , but the child component displays the same text, regardless of Link clicked. I believe it is because ComponentDidMount does not get called in the child component. How would I go about updating/rerendering the child component to get ComponentDidMount to reload?

Channel.js

export default class Channel extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        channelName: 'general'
    };
    this.handleSignOut = this.handleSignOut.bind(this);
}

...

render() {
    return (
        <div className="container">
            <div className="row">
                <div className="channel-list col-lg-2">
                    <h2>Channels</h2>
                    <ul className="list-group">
                        <li><Link to="/channel/general"
                            onClick={() => this.setState({ channelName: 'general' })}>General</Link></li>
                        <li><Link to="/channel/random"
                            onClick={() => this.setState({ channelName: 'random' })}>Random</Link></li>
                    </ul>
                    <div className="footer-segment">
                        ...
                    </div>
                </div>
                <ChannelMessages channelName={this.state.channelName} />
            </div>
        </div>
    );
}

ChannelMessages.js

export default class ChannelMessages extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        channelName: this.props.channelName,
        message: '',
        messages: [],
        textValue: ''
    }
    ...
}

componentWillReceiveProps(nextProps) {
    this.setState({channelName: nextProps.channelName})
}

componentDidMount() {
    this.messageRef = firebase.database().ref('messages/' + this.state.channelName);
    this.messageRef.limitToLast(500).on('value', (snapshot) => {
        let messages = snapshot.val();
        if (messages !== null) {
            let newMessages = [];
            for (let message in messages) {
                newMessages.push({
                    author: {
                        displayName: messages[message].author.displayName,
                        photoURL: messages[message].author.photoURL,
                        uid: messages[message].author.uid
                    },
                    body: messages[message].body,
                    createdAt: messages[message].createdAt,
                    id: message
                });
            }
            this.setState({ messages: newMessages, textValue: newMessages.body });
        }
        console.log(this.state.textValue)
    });
}

componentWillUnmount() {
    this.messageRef.off('value');
}

handleSubmitMessage(event) {
    event.preventDefault();
    let user = firebase.auth().currentUser;
    this.messageRef.push().set({
        author: {
            displayName: user.displayName,
            photoURL: user.photoURL,
            uid: user.uid
        },
        body: this.state.message,
        createdAt: firebase.database.ServerValue.TIMESTAMP
    });
    this.setState({ message: '' });
}

...

render() {
    return (...);
}

}

why are you not using the channelName directly from the props? you are using the componentDidMount event. It runs only once. If you want to run the firebase code whenever a new channel name is passed; extract the fetching logic function and run it in componentDidMount and componentDidUpate(check here if it's different from the previous one)

ComponentDidMount runs only once - more here

ComponentDidUpdate doesn't run on initial trigger - more here

   export default class ChannelMessages extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            message: '',
            messages: [],
            textValue: ''
        }
        ...
    }

    componentDidMount() {
        const {channelName} = this.props;

        fetchFromDb(channelName);
    }

    componentDidUpdate(prevProps) {
        if (prevProps.channelName !== this.props.channelName) {
            fetchFromDb(this.props.channelName);            
        }
    }

    componentWillUnmount() {
        this.messageRef.off('value');
    }

    handleSubmitMessage(event) {
        event.preventDefault();
        let user = firebase.auth().currentUser;
        this.messageRef.push().set({
            author: {
                displayName: user.displayName,
                photoURL: user.photoURL,
                uid: user.uid
            },
            body: this.state.message,
            createdAt: firebase.database.ServerValue.TIMESTAMP
        });
        this.setState({ message: '' });
    }

    ...

    render() {
        return (...);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM