0

So I have an input :

input field

onChange this input's value get's updated and stored in props for persistance but that's unrelated to the issue.

I'm going to add a checkmark next to the field if the entered value is found in the database.

I already have the call to API and answer set up (my redux reducer) :

import EventTypes from '../EventTypes';

const initialState = {
    response: {},
};

export default (state = initialState, action) => {
    switch (action.type) {
        case EventTypes.EXISTS_FULFILLED.type:
            console.log(action.payload.body().data());
            return { ...state, response: action.payload.body().data() };
        default:
            return state;
    }
};

Note that the above console log correctly prints the true/false value (+ sent value. I get the two back from the server).

but in the component that calls this :

const defaultProps = {
    onChange: () => {
    },
    value: { type: '' },
    provider: { type: '' },
};

class InputsComponent extends Component {

    onChange = () => {
        this.props.onChange(this.props.id);
    };

    updateFirstInput(event) {
            const { onChange, exists, response } = this.props;
            const payload = {
                objectType: this.props.placeholder.toLowerCase(),
                objectName: event.target.value,
            };
            exists(payload);
            this.setState({ firstInput: event.target.value });
            onChange({ value: {
                ...this.state,
                firstInput: event.target.value,
            } }, this.props.id);
            setTimeout(() => {
                this.setState({ exitsStore: response });
            }, 200);
            setTimeout(() => {
                console.log(this.state.exitsStore);
            }, 1000);
        }

The console logs are empty the first time "updateFirstInput()" is called and then are always one "onChange" behind from being accurate.

that is for "hello" :

[input value] [ console log of value for which presence on the server is true/false]
"h"              ""
"he"             "h"
"hel"            "he"
"hell"           "hel"
"hello"          "hell"

after awhile I figured out that this was happening.

I've tried this :

const defaultProps = {
    onChange: () => {
    },
    value: { type: '' },
    provider: { type: '' },
};

class InputsComponent extends Component {

    onChange = () => {
        this.props.onChange(this.props.id);
    };

checkExists(object){
    const { exists, response } = this.props;
    const payload = {
        objectType: this.props.placeholder.toLowerCase(),
        objectName: object,
    };
    exists(payload);
    return response;
}

updateFirstInput(event) {
    const { onChange } = this.props;
    this.setState({ firstInput: event.target.value });
    onChange({ value: {
        ...this.state,
        firstInput: event.target.value,
    } }, this.props.id);
    const response = await this.checkExists(event);

    console.log(response);
    this.setState({ exitsStore: response });

    console.log('stored data :');
    console.log(this.state.exitsStore);
}

but for some reason people online suggesting we use await are not providing context. the above is not even compilable. (npm start will fail)

and even before that ESLint indicates that "await is a reserved word expecting newline or semicolon".

but I'm quite sure the above even with correct syntaxe wouldn't work either.

it's probably not await but how do I manage to have a filled in response from server (ideally) within a single function, the same function that calls the input update.

Constrainst :

as far as I know you can't have both onBlur and onChange on the same input so since i'm already using onChange no onBlur.

and I can't call any of this within a parent or other : there are multiple input fields as children and I need to have the value check and checkmark appear actions happen within the child to be able to match the values together.

UPDATE :

and if I simply remove the timers :

const defaultProps = {
    onChange: () => {
    },
    value: { type: '' },
    provider: { type: '' },
};

class InputsComponent extends Component {

    onChange = () => {
        this.props.onChange(this.props.id);
    };

updateFirstInput(event) {
        const { onChange, exists, response } = this.props;
        const payload = {
            objectType: this.props.placeholder.toLowerCase(),
            objectName: event.target.value,
        };
        exists(payload);
        this.setState({ firstInput: event.target.value });
        onChange({ value: {
            ...this.state,
            firstInput: event.target.value,
        } }, this.props.id);
        this.setState({ exitsStore: response });
        console.log(this.state.exitsStore);
    }

...TWO calls behind.

tatsu
  • 2,316
  • 7
  • 43
  • 87
  • to use `await`, function needs to be `async`. you can use both `onChange` and `onBlur` events. Can you add the code for `this.props.onChange` please? – bennygenel Sep 19 '17 at 13:53
  • done. it's part of the same class. and thanks for the info didn't know onChange and onBlur could be used together. – tatsu Sep 19 '17 at 14:00
  • also what do you mean by function needs to be async? my function is decidedly async as is everything in JS. things just get executed in a completely random order (which is annoying IMO) Ive not found any solution to combat this other then add timers – tatsu Sep 19 '17 at 14:03
  • for async please check [this page for examples](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). You did not add the `this.props.onChange` you just added `updateFirstInput` again. Whats happening inside the `this.props.onChange`? – bennygenel Sep 19 '17 at 14:19
  • The execution order is definitely not random, but actually makes perfect sense. Also, the statement that everything is async in JS is decidedly wrong. Please have a look at the [docs for `setState()`](https://facebook.github.io/react/docs/react-component.html#setstate), as they explain how to and how not to access the state right after it has been changed. – TimoStaudinger Sep 19 '17 at 14:27
  • @Timo yeah it's just I'm new to JS and the fact that you can have a line below executed before the line above it hurts my brain. maybe I'll get used to it someday but right now the docs for setstate don't make sense to me. – tatsu Sep 19 '17 at 14:31
  • @bennygenel please pay careful attention : the UPDATE section of my post is entirely unrelated to your comment and happened way after I replied that I had edited my post to include onChange which I had done. the edit you're looking for is way above.... look for : `const defaultProps = {` – tatsu Sep 19 '17 at 14:33
  • How am I supposed to know that you updated the question with UPDATE tag and the update you made for my request is not there but at the beginning of the question? I asked whats happening inside the `onChange` function and you added a default prop declaration with a empty function. What are you sending to `onChange` prop from the parent? To solve a problem you need to first understand the problem. To understand this problem I think you should make some reading on [Sync vs Async](https://stackoverflow.com/q/16336367/2315280) – bennygenel Sep 19 '17 at 14:47
  • the stored values. I can tab out of this view and tab back and despite the total re-construct of the component the inputs will have preserved their values. but as I said in the first few lines of my post this is working functionality that I have no need to edit. – tatsu Sep 19 '17 at 14:51
  • I read the linked stackoverflow. I can't believe he's getting the exact opposite results as me. a semicolon and a newline does not result in a wait. the console logs will be called and not wait for the api call to have returned a value. – tatsu Sep 19 '17 at 14:59
  • see this kind of answer is what I'm looking for : https://www.fullstackreact.com/30-days-of-react/day-15/ it can actually be read and understood by human beings. – tatsu Sep 20 '17 at 07:52
  • that being said having applied that guide the call is still one step behind. – tatsu Sep 20 '17 at 08:10

2 Answers2

1

setState is asynchronous. It looks like you are using timeouts to try to get around this fact, but that's a very crude and brute force way to "solve" the problem and it doesn't actually solve the problem (as shown by your results).

But setState has handlers available when you need to run code "after" it. You do this simply by passing in a callback function as the second argument. E.g.;

this.setState({
    exitsStore: response
}, () => {
    console.log(this.state.exitsStore);
});
Jake Haller-Roby
  • 6,335
  • 1
  • 18
  • 31
  • Nice! It's still is one call behind though. I think that it's because for some reason the first value pulled from the reducer is an empty when pulling it this way. – tatsu Sep 20 '17 at 06:40
0

The handy pre-existing methods of React to the rescue.

In this case : componentWillRecieveProps().

componentWillReceiveProps(newProps) {
    const response = newProps.response;
    const old = this.props.response;
    console.log(response);
    const id = this.props.id;
    if (response !== old && response.objectIdentifier === id) {
        if (response.activ) {
            if (response.isthere) {
                this.setState({ inputIcon: 4 });
            } else {
                this.setState({ inputIcon: 3 });
            }
        } else {
            this.setState({ inputIcon: 2 });
        }
    }
}
onChange = () => {
    this.props.onChange(this.props.id);
};
updateInput(event) {
    const { onChange, exists, response } = this.props;
    const inputValue = event.target.value;
    this.setState({ input: inputValue });
    onChange({ value: {
        ...this.state,
        input: inputValue,
    } }, this.props.id);
    if (inputValue === '') {
        this.setState({ inputIcon: 0 });
    } else {
        const placeHolder = this.props.placeholder.toLowerCase();
        const objectIdentifier = this.props.id;
        const payload = {
            objectType: placeHolder,
            objectName: inputValue,
            objectIdentifier,
        };
        exists(payload);
        this.setState({
            existStore: response,
        };
    }
}

the reason why this works is that componentWillRecieveProps() by default receives a parameter which is the updated props state and this one is indeed the new one. you can check that there has indeed been an update by doing as I did : checking the object you have in props against the passed parameter and only perform your actions if they are different.

I need edits on this post please, because I'm terrible at expressing this with the correct developer terms!

tatsu
  • 2,316
  • 7
  • 43
  • 87