Paul D. Dango
Paul D. Dango

Reputation: 43

TypeError: Cannot read property 'scrollIntoView' of null

class App extends Component {
    constructor(props) {
        super(props)
        this.state = { text: "", messages: [] }
    }
    componentDidMount() {
        const config =  {

        apiKey: "<api-key>",
        authDomain: "<project-name>.firebaseapp.com",
        databaseURL: "https://<project-name>.firebaseio.com",
        projectId: "<project-name>",
        storageBucket: "<project-name>.appspot.com",
        messagingSenderId: "<sender-id>",
    };

    if (!firebase.apps.length) {
        firebase.initializeApp(config);
    }
    this.getMessages()
    var database = firebase.database();
    var ref = database.ref('messages');

}



onSubmit = event => {
    if (event.charCode === 13 && this.state.text.trim() !== "") {
        this.writeMessageToDB(this.state.text)
        this.setState({ text: "" })
    }
}

writeMessageToDB = () => {
    firebase.database().ref('messages/').push({
        text: this.state.text,
        createdAt: createdAt,
        user:{
            _id: currentUser,
            name:name
        }

    });
}

getMessages = () => {
    var messagesDB = firebase
        .database()
        .ref("messages/")
        .limitToLast(500)
    messagesDB.on("value", snapshot => {
        let newMessages = []
        snapshot.forEach(child => {

            var message = child.val()

            var yeah = dateFormat(message.createdAt,"dddd, mmmm dS, yyyy, h:MM:ss TT")
            newMessages.push({ id: child.key, text: message.text,createdAt: yeah, names: message.name })
        })

        this.setState({ messages: newMessages })
        this.bottomSpan.scrollIntoView({ behavior: "smooth" })
    })

}



renderMessages = () => {
    return this.state.messages.map(message => (
        <ListItem>
            <ListItemText className="chatList"
                style={{ wordBreak: "break-word", backgroundColor: "#FFA1B5", borderRadius: "10px", width: "10px", padding: "5px" }}
                primary={message.name+": "+message.text}
                secondary={message.createdAt}

            />
        </ListItem>
    ))


}



render() {
        return (
            <MuiThemeProvider theme={theme}>
                <div style={mainCont}>
                    <label style={labelStyle} className="labelStyle">&nbsp;&nbsp;&nbsp;Chat</label>
                    <div className="App" >

                    <ScrollToBottom className={ ROOT_CSS }>
                        <List>{this.renderMessages()}</List>
                    </ScrollToBottom>
                    <TextField className="txtFieldStyle"
                        autoFocus={true}
                        multiline={true}
                        rowsMax={3}
                        placeholder="Type something.."
                        onChange={event => this.setState({ text: event.target.value })}
                        value={this.state.text}
                        onKeyPress={this.onSubmit}
                        style={{ width: "350px", overflow: "hidden", marginLeft: "15px", fontSize: '63px', paddingBottom: '5px' }}
                    />
                    <span ref={el => (this.bottomSpan = el)} />
                </div>
            </div>
        </MuiThemeProvider>
    )
}
}

export default App;

chat feature is working fine unless the user navigates to other pages and go back to the chat feature and attempts to send message through chat.

Upvotes: 1

Views: 5520

Answers (1)

Oluwafemi Sule
Oluwafemi Sule

Reputation: 38992

The following two lines in App.componentDidMount() are a potential race condition.

    this.setState({ messages: newMessages })
    this.bottomSpan.scrollIntoView({ behavior: "smooth" })

The App state may be mutated earlier to begin a render cycle so that the bottomSpan is set to reference the element before this.bottomSpan.scrollIntoView() is called.

However, this.bottomSpan.scrollIntoView() is never guaranteed to be called after state is updated.
Remember that setState doesn't always immediately mutate the state of the component. [1]

You can scroll the referenced element into view after state is mutated by doing that in a callback passed as the second argument to state.

this.setState(
  { messages: newMessages }, 
  () => this.bottomSpan.scrollIntoView({ behavior: "smooth" })
)

Upvotes: 2

Related Questions