Use MobX #inject with Context API in functional components - reactjs

How can I implement #inject and observer in functional child components without explicitly importing the store with context and destructuring it. Repo and Deployed site below
https://laughing-mayer-7a7c36.netlify.app/
https://github.com/hildakh/testmobx

You can make hook like that, so you won't need to import context every time, just this hook:
export const useStore = () => {
const store = React.useContext(storeContext)
if (!store) {
// this is especially useful in TypeScript so you don't need to be checking for null all the time
throw new Error('useStore must be used within a StoreProvider.')
}
return store
}
And you can still use inject decorator with functional components if you prefer that way, it still works and it's totally fine way

Related

Should we change class component to functional component when we put state into Redux?

I was learning React and created two class components having respective states. Then, I learned about Redux and decided to transfer states into redux store. The question is "Is it best practice to change class componenents into functional components since we get state via props from redux store?"
Functional components with react hooks is the new standard of coding on React. For store management(f.e. redux) you may use as classes as functional components, but most of the libs moved to functional components and you may not use all benefits of last versions of them.
Why I prefer functional components and hooks over classes:
Cleaner render tree. No wrapper components
More flexible code. You
can use useEffect on different state changes, in classes you have
only componentDidUpdate for ANY state/props change
You can define your custom hooks to keep your code clean and shiny
IMHO, yes, I suggest that you should switch from class-based component to functional component as soon as possible.You might not want to know how the class-based components have bugged me so hurt before I decided to go with Hooks. The number of components in my large project is now over 400 (including both smart and dumb components) and keep increasing. Hooks keep my life easier to continue developing and maintaining.
Have a look at this useful article: https://blog.bitsrc.io/why-we-switched-to-react-hooks-48798c42c7f
Basically, this is how we manage state with class-based:
It can be simplified to half the lines of code, achieving the same results with functional component and useState, useEffect:
Please also take a look at this super useful site: https://usehooks.com/
There are many useful custom hooks from the community that you can utilize. Below are the ones that I have been using all the time:
useRouter: Make your life easier with react-router. For example:
import { useRouter } from "./myCustomHooks";
const ShowMeTheLocation = () => {
const router = useRouter();
return <div>Show me my param: {router.match.params.myDesiredParam}</div>;
}
useEventListener: simplify your event handler without using componentDidMount and componentWillUnmount to subscribe/unsubscribe. For example, I have a button that needs to bind a keypress event:
import { useEventListener } from "./myCustomHooks";
const FunctionButton = () => {
const keydownHandler = event => { // handle some keydown actions };
const keyupHandler = event => { // handle some keyup actions };
// just simple like this
useEventListener("keydown", keydownHandler);
useEventListener("keyup", keyupHandler);
}
useAuth: authenticate your user.
import { useAuth } from "./use-auth.js";
const Navbar = (props) => {
// Get auth state and re-render anytime it changes
const auth = useAuth();
// if user is authenticated, then show user email, else show Login
return <div>{auth.user? auth.user.email: "Login"}</div>;
}
useRequireAuth: handle redirect your user if they are signed out and trying to view a page that should require them to be authenticated. This is composed by useRouter and useAuth above.
import { useRequireAuth } from "./myCustomHooks";
// Dashboard is a page that need authentication to view
const Dashboard = () => {
const isAuth = useRequireAuth();
// If isAuth is null (still fetching data)
// or false (logged out, above hook will redirect)
// then show loading indicator.
if (isAuth) {
return <div>Fetching data, please wait!</div>
}
// {...{ isAuth }} is similar to:
// isAuth={isAuth}
return <Dashboard {...{ isAuth }} />
}
Hope this helps!
First of All, States can be used only in Class Component. In React's latest version there's a huge update that allows functional components to declare and use state using React-Hooks. So, the best practice I would personally suggest you is to use Class Component when you use the Redux Store. As you're a beginner, Please use a functional component where you don't use any state or props and just render DOM elements (Note: Functional components can accept props). Once you learn the differences properly, go with React-Hooks.
I hope it helps!! Happy Coding!!

Using constructor items within a function-based component

I'm following along to this PubNub Push Notifications in React-JS tutorial and everything is going fine up until I add the Constructor to the code as it states in the tutorial. I (think I) know enough about react to know that you cannot use a constructor in a non-class based component.
I can't seem to figure out how I would implement this. It appears that when the tutorial was made, the boilerplate code has since been updated and this constructor is no longer supposed to be used in this way.
Below is an image of where I am with the code and in the tutorial:
Should I just convert this to a class-based component? or will that limit me from using hooks in the future?
(Apologies for lack of react knowledge)
You need to use useRef and useEffect hooks:
Why useRef?
The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class. more
this.pubnub is an instance property of a class, so you have to add it to useRef hook.
Why useEffect?
useEffect, adds the ability to perform side effects from a function component more
Initialization of PubNubReact - is a side effect, so you need to add it to useEffect hook.
UPDATE
Bad news: It looks like that pubnub-react can't be used with functional components, they have very weird API and they even agree with this (You could read about that in this issue)
Good news: You could use an official pubnub package in your application and it will work great:
import React, { useRef, useEffect } from 'react';
import PubNub from 'pubnub';
function App() {
const pubnub = useRef(null);
useEffect(() => {
pubnub.current = new PubNub({...});
pubnub.current.addListener({
message(msg) { console.log(msg) }
});
pubnub.current.subscribe({ channels: ['channel1'] });
pubnub.current.publish({ message: 'hello world from react', channel: 'channel1' });
}, []);
return (...);
}

How to mock a custom hook inside of a React component you want to test?

If you have a React component that calls a custom hook that fetches data, what is the best way to mock that internal custom hook result when testing the React component? I see 2 main approaches:
1) Jest.mock the custom hook. This seems to be the most recommended approach, but it seems like it requires the test to have more knowledge of internal implementation details and what it might need to mock than what the props interface of the component might suggest (assuming use of prop-types or TypeScript)
2) Use a dependency injection approach. Declare the hook as a prop, but default it to the real hook so you don't have to set it everywhere you render the component, but allow overriding with a mock for tests. Here is a contrived codesandbox example with a test that mocks a custom hook:
https://codesandbox.io/s/dependency-inject-custom-hook-for-testing-mjqlf?fontsize=14&module=%2Fsrc%2FApp.js
2 requires more typing, but seems easier to work with for testing. However, tests already have to have knowledge of internal implementation details of component to test any conditional logic for rendered output, so maybe that's not important and 1 is the best approach. Is 1 the way to go? What tradeoffs do you see? Am I missing another approach altogether?
To mock your custom hook using jest.
import * as useCustomHook from '../hooks/useCustomHooks'
const spy = jest.spyOn(useCustomHook, 'default')
spy.mockReturnValue({
name: 'test'
})
This question is a few months old, but if you haven't found a good solution, I wrote a package that might help. I went through a similar thought process, including "what if I inject the hooks into the component?" Things got weird.
I basically wanted a connecter to avoid an extra wrapper for presentational components just to test them.
I came up with react-hooks-compose, which lets you keep your hooks and your presenters separate, and test them individually or together: https://www.npmjs.com/package/react-hooks-compose
export const useFetch = () => {
const [user, setUser] = useState();
useEffect(() => {
fetchData('some-url') // <-- Fetches data on mount
.then(res => setUser(res.data));
}, []);
return {user};
}
// composeHooks passes the values from your hooks as props
export const UserPresenter = ({user}) => {
return <div>You fetched data for: {user.name}</div>;
}
export default composeHooks({ useFetch })(DataPresenter);
Now you don't have to mock the hook, you can just test the presenter with a prop:
it('presents user', () => {
const { queryByText } = render(<UserPresenter user={{name: 'Mary'}} />); // <-- Named export
expect(queryByText('Mary')).toBeTruthy();
});
Or, you have the option of a higher-level integration test:
it('fetches data', () => {
fetchData.mockResolvedValue('Mary');
const { queryByText } = render(<UserWithData />); // <-- Default export
expect(queryByText('Mary')).toBeFalsy();
return wait(() => {
expect(queryByText('Mary')).toBeTruthy();
});
});
You can even unit test the hook if you like.
Why don't you mock the underlying method that makes the api call instead?
For example, if you're retrieving data with fetch(), you mock that instead. That way you can define custom response for that call, which will make testing the hook itself easy.
With mocking hook itself you never know if real one works well all together with your component.
With passing hook as a prop it would be really hard to make hooks communicate with each other. E.g. when you need your custom hook to call setter from the same component useState. You would need to extend custom hook with more and more parameters.
You may mock external API call - I mean mocking fetch or XHR. It still needs to know some implementation details - the fact you're running HTTP request - but there are less things your test should know about.

Hooks in React Native: Only Call Hooks from React Functions

There is this section on React Hooks that I don't really understand what it is saying:
Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions. Instead, you can:
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.
What does it mean to only call Hooks from React function components and how is a React function different than what I would call a regular functional component?
In my mind they're the same:
const App = () => {
useEffect(() => //do stuff);
return (
// return some sort of stuff here
)
}
The reason I ask is the eslint I have for hooks is complaining about the way I'm using useState here:
const checkPermissions = () => { //when I change this to 'usePermissions', it works fine
const [locPermission, setLocPermission] = useState(null); // eslint error here
//'React Hook "useState" is called in function "checkPermissions" which
//is neither a React function component or a custom React Hook function.'
//Check for and set state for permissions here
return locPermission;
};
What they mean is the entry point for a set of hooks should be within a React component and not elsewhere if it is used as a hook e.g. in this very arbitrary/simple example here:
my-utils/useCustomHook.js arbitrary custom hook
import { setState } from 'React'
export default function useCustomHook() {
const [state, setState] = useState(()=> 'anyRandomState');
// ...possibly re-using other custom hooks here,
// then returning something for our component to consume
}
MyComponent.js Your React component
import React, { setState } from 'react'
import useCustomHook from 'my-utils/useCustomHook'
function MyComponent() {
const offDaHook = useCustomHook();
return (
<div>
Hi, I'm your component with a custom hook.
I see that the value returned was {offDaHook}.
</div>
);
}
random-other-business-logic.js another file which does other things that does not include rendering
import useCustomHook from `my-utils/useCustomHook.js`
useCustomHook(); // Arbitrarily calling from non React component!
// do other things...
One reason ESLint could/would complain is hooks should be formatted as useXXX e.g. in your case, rather than checkPermissions, something like usePermissionChecker (or useCheckPermissions depending on how you think in code) should get the linter to recognize that this function is a custom hook.
I also agree -- this phrasing could probably be improved -- the custom rules of hooks threw me for a little bit of a loop at first as well. I am not 100% sure why this is the case but this is just what I got from that.
I am not sure if React internally appends other variables to hooks such as counting their instances/prototypes but guessing that if the React team doesn't, they would like to reserve the right to do that going forward. In either case, it is a lot cleaner to separate your React-state code from your non-React business logic and hooks using the useHooks convention as they are a little bit funky with their nuances.
Definitely something interesting to look into and wish I could tell you more, but this is just from another user-world programmer currently.

React-Redux narrowing state for child component

Is there a way to pass some part of state (not the whole state) to the child component of some React component? So that mapStateToProps and getState method in redux middleware would use such a substate?
This is important for developing react-redux logic that is unaware of state structure it is used in. It could be then used in several app places or different applications. Such logic would behave as an independent module and satisfy the principle of encapsulation.
Such concept exist in Knockout.js ("with" operator) and in desktop WPF.NET (passing DataContext to child element).
Thank you!
One possible approach is to use selectors and/or namespaces.
With selector, you can encapsulate the logic of querying the state object.
However, you cannot totally hide redux state's shape from your react-redux logic, since it's subscribed to the whole store, by design. Still, we can use a namespace here.
Example:
// counter.js
const namespace = 'counter'
const actualReducer = (state, action) => action.type === 'INC' ? state + 1 : state;
export default { [namespace]: actualReducer }
export const selector = (state, ownProps) => state[namespace]
// configureStore.js
import reducer from './counter'
const store = createStore(reducer)
export default store
// Component.js
import { selector } from './counter'
const Component = props => <some markup />
export default connect(selector)(Component)
So what do we have? In brief:
Component.js is not aware of state shape and even the namespace;
configureStore.js is not aware of the counter namespace;
counter.js is the only place, that is aware of its namespace in the store.
One can also extract selector to separate file since it's the only place where we have to deal with original state object.
Note that namespace should be unique, or, to be re-usable from app to app, it can be imported or injected.
IMO, the idea of re-using react-redux stuff (i.e. connect(...)) is not very solid. Since usually, the state shape is not going to be the same from app to app
I'd encourage you to keep it explicit, so the underlying component is the real one who should have an API that is as unopinionated as possible.

Resources