question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Render not triggered.

See original GitHub issue

Hi, I have a button component which is on a page. I want to use a prop to enable the button when proper validation has been completed. I can see that true is passed to the IsEnabled Prop but the Render method doesn’t get called again to enable the button. My code looks like this. I have read in other posts that you can just set the field directly instead of calling SetState(). Any suggestions? Thanks in advance.

 private bool _isEnabled; 

internal class SignUpButton : Component
{
 
  public SignUpButton IsEnabled(bool isEnabled)
    {
        _isEnabled = isEnabled;
        return this;
    }

  public override VisualNode Render() <====Doesn't get triggered when _isEnabled is set.
    {
        return new Button()
            .Text("Sign up")
            .Class("NextButton")
            .WidthRequest(300)
            .HCenter()
            .IsEnabled(_isEnabled)
            .OnClicked(OnSignUpButtonClicked);
    }

}

Issue Analytics

  • State:closed
  • Created 2 months ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
adospacecommented, Jul 26, 2023

Ok, I see the problem: you’re adding more than one time the same behavior to the entry and also you aren’t checking the entry reference to be null. This is the fixed code:

new Entry(ent =>
{
    if (ent != null &&
        ent.Behaviors.Contains(_emailValidationBehavior))
    {
        ent.Behaviors.Add(_emailValidationBehavior);
    }
})

BUT SEE BELOW

Using behaviors is not recommended in MauiReactor because they are just another MVVM “thing” to overcome a problem in the MVVM approach that it’s instead solved pretty easily in MVU.

For example, this is how I would rewrite your code in a pure MVU approach:

class MainPageState
{
    public string Email { get; set; }
    public string Password { get; set; }
}

class MainPage : Component<MainPageState>
{
    public override VisualNode Render()
    {
        return new ContentPage
        {
            new VStack(spacing: 20)
            {
                new Label("Create Your Account").Class("LargeLabel").HCenter(),
                new EmailEntry()
                    .OnEmailSet(email => SetState(s => s.Email = email)),
                new PasswordEntry()
                    .OnPasswordSet(password => SetState(s => s.Password = password)),
                new SignUpButton()
                    .PageToNavigateTo("SignUpPage")
                    .IsEnabled(State.Email != null && State.Password != null),
             
            }.Padding(10).Margin(0, 80, 0, 0)
        }.Class("Page");
        
    }
}

class EmailEntryState
{
    public bool IsValid { get; set; }
}

class EmailEntry : Component<EmailEntryState>
{
    private static readonly Regex _validationRegex = new(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");
    private bool _isThinIcon;
    private Action<string> _onEmailSet;

    public EmailEntry UseThinIcon(bool useThinIcon)
    {
        _isThinIcon = useThinIcon;
        return this;
    }

    public EmailEntry OnEmailSet(Action<string> action)
    {
        _onEmailSet = action;
        return this;
    }

    public override VisualNode Render()
    {
        return _isThinIcon ? RenderThinIcon() : RenderRegularIcon();
    }

    private VisualNode RenderThinIcon()
    {
        return new Border
        {
            new Grid()
            {
                new Entry()
                    .Placeholder("Email").Class("NormalEntry")
                    .HFill()
                    .GridColumn(0)
                    .IsTextPredictionEnabled(false)
                    .OnTextChanged(OnEmailTextChanged)
                    .TextColor(State.IsValid ? Colors.White : Colors.Gray)
                    ,

                new Image()
                    .Source("message_icon_thin")
                    .GridColumn(1)
                    .HEnd(),
            }.Columns("*,Auto")
        }.Class("ControlBorder");
    }

    private VisualNode RenderRegularIcon()
    {
        return new Border
        {
            new HStack(spacing: 5)
            {
                new Image().Source("email_icon"),
                new Entry()
                    .Placeholder("Email").Class("NormalEntry")
                    .WidthRequest(300)
                    .IsTextPredictionEnabled(false)
                    .OnTextChanged(OnEmailTextChanged)
                    .TextColor(State.IsValid ? Colors.White : Colors.Gray)
            }
        }.Class("ControlBorder");
    }

    private void OnEmailTextChanged(object sender, MauiControls.TextChangedEventArgs args)
    {
        SetState(s => s.IsValid = _validationRegex.IsMatch(args.NewTextValue));

        _onEmailSet?.Invoke(State.IsValid ? args.NewTextValue : null);
    }
}

class PasswordEntryState
{
    public bool IsValid { get; set; }
}

class PasswordEntry : Component<PasswordEntryState>
{
    private Action<string> _onPasswordSet;

    public PasswordEntry OnPasswordSet(Action<string> action)
    {
        _onPasswordSet = action;
        return this;
    }

    public override VisualNode Render()
    {
        return new Border
        {
            new HorizontalStackLayout(spacing: 5)
            {
                new Image().Source("password_icon"),
                new Entry()
                    .Placeholder("Password")
                    .Class("NormalEntry")
                    .IsPassword(true).WidthRequest(300)
                    .OnTextChanged(OnPasswordTextChanged)
                    .TextColor(State.IsValid ? Colors.White : Colors.Gray)
            }
        }.Class("ControlBorder");
    }

    static bool ValidatePassword(string password)
    {
        const int MIN_LENGTH = 8;
        const int MAX_LENGTH = 15;

        if (password == null) throw new ArgumentNullException();

        bool meetsLengthRequirements = password.Length >= MIN_LENGTH && password.Length <= MAX_LENGTH;
        bool hasUpperCaseLetter = false;
        bool hasLowerCaseLetter = false;
        bool hasDecimalDigit = false;

        if (meetsLengthRequirements)
        {
            foreach (char c in password)
            {
                if (char.IsUpper(c)) hasUpperCaseLetter = true;
                else if (char.IsLower(c)) hasLowerCaseLetter = true;
                else if (char.IsDigit(c)) hasDecimalDigit = true;
            }
        }

        bool isValid = meetsLengthRequirements
                    && hasUpperCaseLetter
                    && hasLowerCaseLetter
                    && hasDecimalDigit
                    ;
        return isValid;

    }
    private void OnPasswordTextChanged(object arg1, MauiControls.TextChangedEventArgs arg2)
    {
        SetState(s => s.IsValid = !ValidatePassword(arg2.NewTextValue));

        _onPasswordSet?.Invoke(State.IsValid ? arg2.NewTextValue : null);
    }
}
0reactions
danlfarnellcommented, Jul 27, 2023

Thanks Ado, that did the trick!!!. I prefer the MVU approach over MVVM with XAML any day. I will go with the full MVU approach you suggested. Thanks again and great work on this project!!!.

Read more comments on GitHub >

github_iconTop Results From Across the Web

react setState() not triggering re-render
React components automatically re-render whenever there is a change in their state or props. In your example, sortedPlans.sort is sorting the ...
Read more >
React doesn't always trigger a re-render on setState
For new React developers, it is common to think that setState will trigger a re-render. But this is not always the case. If...
Read more >
Why react does not re-render component when state is ...
I think it is because React only partially renders depending on what changes. The quickest way to fix this would be to call...
Read more >
How and when to force a React component to re-render
The component did not change, so there was no re-rendering trigger. Here's why. React evaluates state changes by checking its shallow equality ( ......
Read more >
React re-renders guide: everything, all at once
There are four reasons why a component would re-render itself: state changes, parent (or children) re-renders, context changes, and hooks ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found