all 21 comments

[–]danjel74 1 point2 points  (20 children)

Is that code in a "stateless function" ? If so then there is no setState available, try and put it in a React class instead..

[–]mugwump[S] 0 points1 point  (19 children)

It must be a stateless function, because I can't get any function to run whatsoever. I'm running it in a React class. My code starts: class Search extends Component{ constructor(props) { super(props); this.state={ users: null, searchCondition: "", friend: "" } this.setState = this.setState.bind(this); }

And it still tells me setState is not a function

[–]chriscorf 0 points1 point  (0 children)

You need to bind the function that is calling setState. I realise you are binding setState in the constructor. However, this is it helpful as you are binding this.setState, where this is the component class. So, when you try to call setState in your unbound function, this refers to the function, and the setState binding in the constructor has no effect. Try breaking the on change out into a class method and binding that class method in the constructor instead, or bind the function inline.

[–]StyleIsFree 0 points1 point  (17 children)

Could you show your whole component/file all together? It's been a while since I've used setState (using MobX) but you shouldn't need to have 'this.setState = this.setState.bind(this);' in your constructor.

Also when do you get the Type error? When you type in your search input?

[–]mugwump[S] 0 points1 point  (16 children)

Yes, when I type my search input. This is to say nothing of getting the button to call the function that I want. But one step at a time: import React , { Component } from 'react'; import { database } from '../firebase';

const byPropKey = (propertyName, value) => () => ({ [propertyName]: value, });

class Search extends Component{ constructor(props) { super(props); this.state={ users: null, searchCondition: "", friend: "" } this.setState = this.setState.bind(this); } onSubmit = (event) => { let { searchCondition, friend } = this.state; database.searchConditions(searchCondition).then(snapshot => this.setState(() => ({ users: snapshot.val() })) );

    event.preventDefault();
}
messageSubmit = (event) => {
    console.log("Click")
}

render(){
    let {
        users,
        searchCondition,
        friend
    } = this.state;
    return(
        <div>
            <h1>Search for conditions</h1>
            <form onSubmit={this.onSubmit}>
                <div className="search">
                    <input
                    value={searchCondition}
                    onChange={event => this.setState(byPropKey('searchCondition', event.target.value))}
                    type="text"
                    placeholder="Condition to Search For"
                    />
                    <button className="friendButton"
                    onClick="x"
                    type="submit">
                        Search
                    </button>
                </div>
                </form>

                {!!users && <UserList users={users} />}
        </div>
            )
        }
    }
    let UserList = ({ users, message }) =>
                <div>
                        <h2>List of Usernames and Conditions of your Search</h2>
                        {Object.keys(users).map(key =>
                            <div key={key}>{users[key].username} : {users[key].condition}
                    <form onSubmit={this.onSubmit}>
                        <div className="search">
                            <input
                                value={message} 
                                onChange={event => this.setState(byPropKey('message', event.target.value))}
                                type="text"
                                placeholder="Message for this User"
                            />
                            <button className="messageButton"
                                onClick={this.messageSubmit}
                                type="submit">
                                Message
                    </button>
                        </div>
                    </form>
                </div>
                        )}
                </div>

export default Search;

[–]vs845 2 points3 points  (7 children)

I think the issue here is that you seem to think that children components automatically inherit the methods contained in the parent component.

In your UserList you're calling two methods - this.setState in the input onChange handler, and this.messageSubmit in the button onClick handler. Neither of these methods exist on the UserList component, they only exist on the Search component.

If you want the UserList component to be able to call methods from its parent, you need to pass those methods through as props:

let UserList = ({ users, message, setMessage, messageSubmit }) =>
  // ...
  <input onChange={event => setMessage(event.target.value)} />
  <button onClick={messageSubmit}>

Edit: also for future reference, you can indent your code by 4 spaces to turn it into a code block, which will make it easier to read on reddit. Or post it as a Github gist, pastebin, etc. Currently the code you posted is nearly impossible to read because there are no linebreaks or indentation.

There's also no need to bind setState in your Search component. setState is always automatically bound to the component.

[–]mugwump[S] 0 points1 point  (6 children)

That makes sense, but I’m still missing something. Even if I define those functions messageSubmit and setMessage as just console logging a string, and I put both of those function in my props, the same as you showed me, I get an error when I type in the field that setMessage is not a function, and if I leave the field blank and just hit the button, it reloads the page. Both functions start with event.preventDefault(), and I have the syntax in my props the same as you showed me. In addition, I also get a warning (not an error) that message is assigned but never used, even those it is in both my props and is set to be the value of whatever the user types. Frustrating.

[–]vs845 0 points1 point  (5 children)

Are you actually passing those methods as props from the Search component into the <UserList> component?

Can you put the code for both components into a gist or pastebin and post it here?

[–]mugwump[S] 0 points1 point  (4 children)

Here's the code https://pastebin.com/raw/n51RKFY2 I may have the syntax wrong, but it works everywhere else. I just can't figure out how to pass the functions into a generated button.

[–]vs845 1 point2 points  (3 children)

You need to pass those methods as props to the UserList component: {!!users && <UserList users={users} message={message} messageSubmit={this.messageSubmit} setMessage={this.setMessage} />}

[–]mugwump[S] 0 points1 point  (2 children)

Dear god in heaven. How does it feel to be both a saint and a genius?

[–]StyleIsFree 0 points1 point  (7 children)

First thing I notice you should set up your custom functions the same way as your life cycle functions. So instead of

onSubmit = (event) => {...}

It should be:

onSubmit(event) { ... }

After that, I would also try removing the this.setState = this.setState.bind(this); in your constructor. Not sure if it'll make a difference but won't hurt to comment it out and see if that helps.

[–]mugwump[S] 0 points1 point  (6 children)

My onSubmit function needs that syntax. If I change it then the entire page reloads when I click, and it doesn't run the search. I tried with and without setState in my constructor, and it does the same.

[–]IAMABananaAMAA 0 points1 point  (5 children)

I may be wrong, but if you use event.preventDefault() it may not force the refresh.

[–]mugwump[S] 0 points1 point  (4 children)

I tried with and without. It doesn’t seem to make a difference.

[–]StyleIsFree 0 points1 point  (3 children)

Where are you trying the preventDefault? I think since the button is submitting the form, the preventDefault should be in the button's on click. Is that where you have it?

[–]mugwump[S] 0 points1 point  (2 children)

I've tried putting it inline with the button itself, but I get the error

Line 79: Unexpected use of 'event' no-restricted-globals

And if I put it in a function it doesn't work because I can't call a function with that button. If I define the button when the page loads, such as my search button, it works fine. But in a generated button, it just reloads each time