I used to have a component like this:
class BlahDumb extends Component {
toggleMe = () => console.log('toggling')
render() {
...
}
}
I would then use it like this:
class App extends Component {
doIt = () => this.el.toggleMe()
refEl = el => this.el = el;
render() {
return (
<div>
<BlahDumb ref={this.refEl} />
<button onClick={this.doIt} />
</div>
)
}
}
Now this worked well UNTIL I connected Blah it to redux.
I changed Blah to this:
const Blah = connect(function() { ... })(BlahDumb);
Now I can no longer access toggleMe from this.el that I got via ref. Is this bad pattern? Or is there a way to get a childs refs?
Connect your child component passing options argument to connect like
const Blah = connect(
mapStateToProps,
mapDispatchToProps,
null,
{ withRef: true }
)(BlahDumb)
And in your parent component call the method in child using getWrappedInstance() function like.
this.el.getWrappedInstance().toggleMe()
Related
I'm using #types/xrm and attempting to test a method call with sinon. Unfortunately I am hitting quite a few issues due to the complex nature of the return and call I need to mock. I can find really simple examples of sinon stubbing or spying on calls, but nothing more complex than that.
I have the following simple code:
export class AccountForm {
static Fields = class {
static PrimaryContact = "primarycontactid";
static Name = "name";
}
public onLoad(context: Xrm.Events.EventContext): void {
// Get the form context
const formContext = context.getFormContext();
// Get the attributes required
const primaryContact = formContext.getAttribute(AccountForm.Fields.PrimaryContact);
const name = formContext.getAttribute(AccountForm.Fields.Name);
// Add our onchange events
primaryContact.addOnChange(this.onChangePrimaryContact);
name.addOnChange(this.onChangeName);
}
public async onChangePrimaryContact(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something");
}
public async onChangeName(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something else");
}
}
I want to test that an onchange event has been registered to both fields. Ideally, I'd like to check it's the RIGHT onchange, but I'll settle with the fact that it's been called once.
The "easy" way has been to check that the addOnChange method was called twice, this is as below:
import {AttributeMock, XrmMockGenerator} from "xrm-mock";
import * as sinon from "sinon";
import { AccountForm } from "../../src/entities/Account/Form";
describe("Account Form Tests", () => {
describe("Check Onload", () => {
beforeEach(() => {
XrmMockGenerator.initialise();
XrmMockGenerator.Attribute.createString("name", "");
XrmMockGenerator.Attribute.createLookup("primarycontactid", []);
});
it("should register onChange functions", () => {
// Arrange
let formContext = XrmMockGenerator.getFormContext();
let context = XrmMockGenerator.getEventContext();
// Stub
const attributeStub = sinon.stub(AttributeMock.prototype, "addOnChange");
// Act
let form = new AccountForm();
form.onLoad(context);
// Assert
expect(attributeStub.calledTwice).toBeTruthy();
});
});
});
But this is not very resilient, as it is not checking WHICH attributes the onChange functions were added to, or what function was registered.
I've tried stubbing the ForContext's "GetAttribute", but looks like it's requiring me to mock the entire return object, as otherwise, the stub does not return anything? I can get around this with using spy, but still can't work out how to check the attribute that the onChange is being added to and what the function is
Am I missing something obvious here?
This is a class component that is using state and setState, but i would like to use useState. Also, aside from being able to use a functional component rather than a class base component, what are the benefits?
import React, {Component} from "react"
class Toggler extends Component {
state = {
on: this.props.defaultOnValue
}
toggle = () => {
this.setState(prevState => {
return {
on: !prevState.on
}
})
}
static
defaultProps = {
defaultOnValue: false
}
render() {
return (
<div>
{this.props.render(this.state.on, this.toggle)}
</div>
)
}
}
export default Toggler
So far...
import React, {useState} from "react"
function Toggler() {
const [on, setOn] = useState(this.props.defaultOnValue)
toggle = () => {
setOn( {/* what should go here? */} )
}
static
defaultProps = {
defaultOnValue: false
}
return (
<div>
{this.props.render(on, toggle)}
</div>
)
}
export default Toggler
Toggling your state value is as simple as inverting the current value returned from useState.
Functional components receive the props passed to them as an argument, so no need to use this.props. Also, to set static properties on a functional component you can just set them on the function itself.
function Toggler(props) {
const [on, setOn] = useState(props.defaultOnValue)
const toggle = () => {
setOn(!on)
}
return <div>{props.render(on, toggle)}</div>
}
Toggler.defaultProps = {
defaultOnValue: false
}
export default Toggler
In terms of benefits, it's up to you to decide whether you think it's worth it for your case. There's no specific benefit to using useState rather than a class based component, however if you want to start using other hooks, or integrate with third party libraries using hooks, you may wish to convert some of your components to functional ones where necessary.
I'm using Akavache's GetAndFetchLatest method and I have created dependency services to communicate with Akavache's method. I'm calling akavache from service layer successfully when i directly reference. For subscribing
MyMod result = null;
var cache = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => GetInfo(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//do sothing.
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
It works.But how an I call subscribe on service layer with interface/dependency service?
I think you are new to reactive programming. Understanding the basic principles helps when using Akavache. Maybe this intro helps.
To answer your question, place code like this in your "repository" class:
public override IObservable<MyClass> Get(string key)
{
var cachedObservable = blobCache.GetAndFetchLatest<MyClass>(key,
() => GetFromServerAsync(key));
return cachedObservable ;
}
And in the caller:
private void getNewData()
{
var myClassObservable = myRepository.Get("the key");
myClassObservable.Subscribe(handleNewMyClass);
}
private void handleNewMyClass(MyClass newClass)
{
//handle the new class
}
Note that handleNewMyClass() is called twice:
first with the MyClass from cache
then with the MyClass that was fetched (from the server)
Using this approach you can simply place the repository class in your IoC Container.
Please find the the sample code :
var result = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => ViewModelLocator.GetInstance<IYourServiceName>().MethodName(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//Your piece of code
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
Please note the there any anything inside subscribed will be called twice : first set of data will be cache and second set will be freshly fetched from server.You have to manage according.
So I have a user-list component and a user-detail component.
The user-list component has an md-table listing all users registered. The user is able to click on a button to see the details of the corresponding entity.
After editing the Name property and saving (for example), the user-detail redirects to the user-list component. But the md-table displays the old information.
How can I trigger an md-table refresh after editing the entity in another component like this scenario?
Here is my user-list component:
export class UserListComponent implements OnInit {
tableDisplayedColumns: string[] = ['userName', 'email', 'options'];
userDataSource: UserDataSource;
constructor(private _userService: UserService, private _router: Router) { }
ngOnInit(): void {
this.userDataSource = new UserDataSource(this._userService);
}}
class UserDataSource extends DataSource<UserVModel> {
constructor(private _userService: UserService) {
super();
}
connect(): Observable<UserVModel[]> {
return this._userService.getAllUsers();
}
disconnect(): void { }}
The table will re-render when the stream provided by connect() emits a new value.
getAllUsers needs to emit a new set of data when it is changed. Otherwise, listen for a separate stream (e.g. dataChanged) from the _userService and use that to call getAllUsers again.
connect(): Observable<UserVModel[]> {
return this._userService.dataChanged
.switchMap(() => this._userService.getAllUsers()
}
Actually the problem was that the User-Detail component was redirecting before the observable had the chance to complete. So I placed the router.navigate as a complete function.
Code Before
onSubmit() {
this._updateSub = this._service.updateUser(this._id, this._userForm.value)
.subscribe(null, err => this.onSubmitError(err));
this._router.navigate(['/user']);
}
Code After
onSubmit() {
this._updateSub = this._service.updateUser(this._id, this._userForm.value)
.subscribe(null, err => this.onSubmitError(err), () => this.afterSubmit());
}
afterSubmit() {
this._router.navigate(['/user']);
}
Prior to this change, I was getting the old values after the redirect. Using the complete function, I get up to date values without the need to use a dataChanged observable in the service.
You could tidy things up and put your router navigation inside the response :
onSubmit() {
this._updateSub = this._service.updateUser(this._id,
this._userForm.value).subscribe(
res => this._router.navigate(['/user']);
err => this.onSubmitError(err), () => this.afterSubmit());
}
Following is my component, where I want to display image using Promise. Following is my code, browser got unresponsive when it run.
Can any one help me to fix this issue.
HTML:
<img [src]="getImage()" />
JS: my.component.ts
// ...
public imageUrl:string;
constructor() {
this.imageUrl = "path-to-my-image/image.png";
}
getImage(){
return new Promise((resolve,reject) =>{
// this.imageUrl is not static one, it may fetch from the server
resolve(this.imageUrl);
});
}
// ...
When you use a function call as you are for the value of src, Angular's change detection mechanism will call it over and over again. In your case, that will kick off the server calls hundreds or thousands of times.
What you want is something along the lines of:
<img [src]="image | async">
ngOnInit() {
this.imageUrl = "path-to-my-image/image.png";
this.image = this.getImageUrl();
}
getImageUrl() {
return new Promise((resolve,reject) =>{
// this.imageUrl is not static one, it may fetch from the server
resolve(this.imageUrl);
});
}
However, this will work only once. In other words, if you call getImageUrl again, nothing will happen. What you probably want is to make the URLs into a stream (observable):
<img [src]="image$ | async">
ngOnInit() {
this.imageUrl = "path-to-my-image/image.png";
this.image$ = this.getImageUrl();
}
getImageUrl() {
return Observable.of(this.imageUrl);
}