How to use PureRenderMixin (React js) in Meteor - javascript

I am building an app with Meteor 1.2 and react, and the react-meteor-data mixin to get the data from my publish methods.
I think a good idea is to have an "AppWrapper" component with all the state and data subscriptions, that passes via props to the "App" component, who will render all the components down to the hierarchy...
Although this is working, I would like to have all the render components pure and with immutable data.
What is the way to proceed?
1) There is already the addon in the react package but I don't know how to use it?
2) I should install it from npm and wait for Meteor 1.3 package system?
3) Is it possible to implement a custom shouldComponentUpdate with some immutable library?
I will appreciate any ideas or experience in this topic, thanks

1) Go to point 3
2) Go to point 3
3) Of course! As an example: Immutable.js
const AppWrapper = React.createClass({
mixins: [ReactMeteor.Mixin],
getMeteorState () {
return {
data: Immutable.Map({
userId: Meteor.userId()
})
};
},
shouldComponentUpdate (nextProps, nextState) {
return !this.state.data.equals(nextState.data);
},
render () {
return (
<App data={this.state.data} />
);
}
});
// This is basic example with pure component.
// Of course this might be another component created with
// React.createClass implementing it's own shouldComponentUpdate method.
const App = ({ data }) => (
<p>Your userId: {data.get('userId')}</p>
);

Related

Vue Testing Library: How to mock Vue component

I used React Testing-Library, but not Vue Testing-Library before https://testing-library.com/docs/vue-testing-library/intro/ and I don't want to use mount or shallowMount where I can provide a stub. I want to figure out how to do that in VTL.
We usualy have components that use other components. So when testing our component say ComponentA it may use ComponentB and ComponentC. While there are some components that we want to be rendered (ComponentA and ComponentB) and want to test how they interact with our component, there are also others (ComponentC), which we may not want to test here, because they have their own tests. I.e. we want to "shallow-render" ComponentC when testing ComponentA.
Using react testing library we can do this:
import * as mockedComponentCModule from '....ComponentC';
jest.spyOn(mockedComponentCModule, 'ComponentC').mockImplementation(() => (
<div data-testid="mocked-component-c" />
));
or I like this
jest.mock('path/to/my/ComponentC', () => ({
ComponentC: function ComponentC() {
return <div data-testid="mocked-component-c" />;
}
}));
So in react when we do render(<ComponentA />) the ComponentC will render as a simple div instead of the actual component.
My question is - how can I do this with Vue Testing Library?
I think I figured it out:
jest.spyOn(mockedComponentCModule.default, 'render').mockImplementation((create) =>
create('div', { attrs: { 'data-testid': 'mocked-component-c' } }, 'Test Content')
);
Now sure if there's a better way - I'd be thankful if someone points me in the right direction

Persist data between two pages with Next.js

I would like to refactor my Next.js webapp to have different pages handle different screens. Currently, I have this component holding several states to know in which screen I'm in. In the jsx section, I'm using {value && ... } to render the right component.
But I feel this is not good design, and won't be maintainable when adding more and more screens.
I would also like to avoid Redux as it is overkill for my project.
I was thinking about persisting data in cookies so I can retrieve them with getInitialProps in every component when rendering a new page, but is there a more elegant way?
I've read about tweaking the _app.js but I'm not sure to understand the consequences of doing so, and how it could help me..
Any suggestion?
When multiple of your pages need to make use of same data, you can make use of Context to store the result. It a good way to make a centralized storage without using complex and more self sufficient libraries like redux
You can implement context inside of _app.js file which must reside inside your root folder. This way next.js treats it as a root wrapper and you would just need to use 1 instance of Context
contexts/appContext
import React from 'react';
const AppContext = React.createContext();
export const AppProvider = AppContext.Provider;
export const AppConsumer = AppContext.Consumer;
export default AppContext;
_app.js
import React from 'react'
import App from 'next/app'
import AppProvider from '../contexts/appContext';
class MyApp extends App {
state={
data:[]
}
render() {
const { Component, pageProps } = this.props;
// You can implement logic in this component to fetch data and update state
return (
<div>
<AppProvider value={this.state.data}> // pass on value to context
<Component {...pageProps} />
</AppProvider>
</div>
)
}
}
export default MyApp
Now further each component can make use of context value by using AppConsumer or using useContext if you use hooks
Please read more about how to use Context here

React Context API not working from custom NPM component library

I've built a ReactJS component library that I use for multiple projects installed via an NPM package using a sim link. I want to use the context API to pass data from a parent component served from the component library to my base project to be consumed by multiple consumer components also served from the component library. When I try the context is always undefined in my child components.
If I place my consumer component in my provider component within my library it works like a champ but this defeats what I'm trying to achieve. If I export both the provider and the consumer to my base project the consumer doesn't see the provider.
This is from my base project
import { Screen, COD, GenericSocketServer } from 'component-library'
export default class View extends React.PureComponent {
render() {
return (
<Screen className="screen odmb1">
<GenericSocketServer>
<COD />
</GenericSocketServer>
</Screen>
)
}
}
This is my provider code exported from my 'component-library'
import React from 'react';
import MyContext from "./context";
import COD from './../cod';
export default class GenericSocketServer extends React.Component {
render() {
return (
<MyContext.Provider value={{ foo: 'bar' }}>
<COD />
{this.props.children}
</MyContext.Provider>
);
}
}
This is my content code used in 'component-library'
import React from 'react'
const MyContext = React.createContext()
export default MyContext
This is my consumer component exported from 'component-library'
import MyContext from "../GenericSocketServer/context"
class COD extends React.Component {
render() {
return (
<React.Fragment>
<MyContext.Consumer>
{(context) => {
/*
context comes back undefined
I expect { foo: 'bar' }
*/
console.log('context :', context)
return (
<p>This should work</p>
)}}
</MyContext.Consumer>
</React.Fragment>
)
}
}
Context always comes back undefined as if it doesn't see the parent provider. I think I'm ether doing something wrong initializing the context myself or for some reason the two components I'm importing just don't share the same context. Please help!! Not sure if I should give up on this and just use redux.
Maybe you are making multiple instances of the component providing the context. Let's say you have a component Sound, which starts by:
const { Provider, Consumer } = React.createContext();
If you import this library from your main project, the context will be created at the global space. You then use it to render your document tree. But in another component you also imported this library, which had to be resolved during webpack transpilation. It thus has its own copy of the above lines and a context object created in its own space. The problem occurs when you try to use the Consumer, because the Provider was only made by the main project for the first context object, and the second context's provider instance was never instantiated, thus returns undefined.
A solution to the problem is to enforce a single context object, which you can achieve by telling the second component's webpack that the provider-owning library is an external, so when webpack reaches e.g. the "import sound" line, it will not go further and will assume this dependency is resolved at runtime. When runtime comes, it will take it from the same place where the main project is taking it. To do this in webpack, e.g. for above "sound" library, add this to your other component (not main project):
{
...
externals: {
...
'sound': 'sound'
}
...
}
Also in your component package.json:
{
...
peerDependencies: {
"sound": "^1.2.3"
}
}
Apart from Darko's answer, esm and cjs export is also a possible reason for context to fail in a package. If you use the hook in esm and the provider in cjs, you will not get the value for that context.
I recently had a similar issue where I was trying to consume the value of a context inside my library components but using the provider (imported from the package) in the host app.
I managed to solve the issue just by making react and react-dom external and peerDependencies when bundling in rollup.
should your code of consumer be
<React.Fragment>
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
</React.Fragment>
as stated from the official react doc : https://zh-hant.reactjs.org/docs/context.html
when you define
you can use it like

Does React not explicitly export forwardRef?

UPDATE: There is nothing wrong with the implementation below. The error was due to a react-redux upgrade, as redux now relies on functional components instead of class components.
import React, { forwardRef } from 'react'
const DivWithRef = forwardRef((props, ref) => {
return(
<div ref={ref} {...props}>
{props.children}
</div>
)
})
class AClassComponent extends Component {
render() {
return (
<DivWithRef ref={me => this.node = me} />
)
}
}
When I try this I get the error:
Warning: Function components cannot be given refs.
Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Does react not explicitly export forwardRef?
I have been using this code for a while, and it never caused any issues. After I updated to React 16.8, I started getting the warning above, but my app can still access the DOM Ref even though react claims it shouldn't be able to.
You want to do this instead:
constructor() {
this.node = React.createRef();
}
// more code
render() {
return <DivWithRef ref={this.node} />;
}
The rest of your code is correct.
I answered it on my own. It's not a problem with my implementation. Callback refs are a valid implementation, as explained here: reactjs.org/docs/refs-and-the-dom.html#callback-refs
The reason for the error is that the latest version of react-redux uses functional components instead of class components. After I updated to react-redux, my refs on connected components return the error.
The second ref argument only exists when you define a component with React.forwardRef call.
React.forwardRef

Construct React Component from a React Element

I'm trying to create a "higher-order" function in React that performs some permissions-based checks on the wrapped component and returns it accordingly.
MyComponent.js
...
export default Permissions(MyComponent)
Permissions.js
export default function Permissions(Component) {
class NewComponent extends React.Component {
// ... perform checks here
render() {
return {validPermissions && <Component />}
}
}
}
However, I'd like to be able to use this Permissions as a React Component (as opposed to a function that wraps the component export).
It would looks similar to this:
<Permissions>
<MyComponent />
</Permissions>
When I run React.Component.isPrototypeOf(Component.children) I get false in these instances. My inclination is to think that the solution is to use some React or ReactDOM method to transform the React Element into a React Component, and then perform the same checks.
How can I transform a React Element into a React Component?
Update:
I gave the bit about permissions as context, but not looking for help with regard to implementing permissions.
I am basically looking for the opposite of React.createElement(MyComponent).
You can use a functional component, which combines the best of both worlds: it's simple (just a function!) and at the same time it's a proper stateless React component.
const Permissions = ({ granted, children }) =>
granted ? React.Children.only(children) : null;
Usage:
<Permissions granted={true}>
<MyComponent />
</Permissions>

Categories

Resources