Here is my React Native 0.68 function component with 2 useEffect:
export default MyComp = ({ navigation, route}) => {
const _ref = useRef({});
//1st useEffect
useEffect(()=> {
let cb = {a:1, b:2, c:3};
_ref.current = cb; //in async way which are not showing here
}, [var1,var2]);
//2nd useEffect
useEffect(()=>{
console.log(`${_ref.current.a} ${_ref.current.b}, ${_ref.current.c}`);
},[]);
}
My understand is that 1st useEffect is always executed before the 2nd and console.log in 2nd useEffect shall output 1,2,3. However what the actual console output is undefined, undefined, undefined. What is missing here and how to pass the value of cb to inside of 2nd useEffect?
Related
Learning redux/react-redux, I'm using useSelector with inner selector function in a separate file. It's working perfectly but I have question about best practices.
Assuming that I have a state with 3 entries (firstname, lastname, email), is it better to :
1. Have a specific selector for each case?
selector.js
export const selectFirstname = (state) => state.currentUser.firstName
export const selectLastname = (state) => state.currentUser.lastName
export const selectEmail = (state) => state.currentUser.email
component.js
const firstName = useSelector(selectFirstname)
const lastName = useSelector(selectLastname )
const email = useSelector(selectEmail)
2. Have a generic selector with param?
selector.js
export const selectItem = (key) => {
return (state) => state.currentUser[key]
}
component.js
const firstName = useSelector(selectItem('firstName'))
const lastName = useSelector(selectItem('lastName'))
const email = useSelector(selectItem('email'))
3. Have a global selector and use it with destructuring in my component?
selector.js
export const selectItem = (state) => state.currentUser
component.jsx
const {firstName, lastName, email} = useSelector(selectItem)
Thank you in advance
No, you shouldn't be doing #3. You should be returning the smallest amounts of data per the docs: https://redux.js.org/tutorials/fundamentals/part-5-ui-react#using-multiple-selectors-in-a-component
So 1 or 2 is your best option. Also, anytime currentUser changes the whole thing will re-render and if you are only using 3 of the values, why re-render the component when the values have not changed.
By the sake of understanding better createSelector, I'm trying to use it instead of useSelector
const domainEntitlements = useSelector((state) => state.objects.domainEntitlements[match.params.id]);
const domainEntitlement = createSelector((state) => state.objects.domainEntitlements, (domainEntitlements) => (domainEntitlements));
console.log(domainEntitlements, domainEntitlement);
the useSelector logs an object (correct), but the createSelector logs an equalityCheck function.
Am I doing something very stupid or why I can get the data with useSelector and not with createSelector?
CreateSelector returns a function, this function you can pass to useSelector as an argument.
CreateSelector is used to specify logic to where data is located in the state or how data is derived only once and re use them in other selectors. Here is how you can use your selector in the component:
const selectObjects = (state) => state.objects;
const selectDomainEntitlements = createSelector(
[selectObjects], //re use selectObjects
(objects) => objects.domainEntitlements
);
const createSelectDomainEntitlementById = (id) =>
createSelector(
[selectDomainEntitlements], //re use selecting domain entitlements
(domainEntitlements) => domainEntitlements[id]
);
//example of using the selector in your component
const Component = () => {
const domainEntitlement = useSelector(
createSelectDomainEntitlementById(match.params.id)
);
};
A more detailed explanation on selectors and how to possibly optimize your app by using memoization can be found here
The example code for creating a selector that uses the props is as follows:
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos()
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}
return mapStateToProps
}
connect(makeMapStateToProps)(Component);
However, this means the memoized selectors will be lost after the component gets unmounted (unlike the selectors defined in a file).
Is there a way in Reselect library to save these selectors?
If not, what would be the best approach of saving them? I am thinking of caching, but I would need one selector for every value.
The difference between switchMap and switchMapTo is that switchMap transforms each source emission into observable upon the emission and switchMapTo ignores emitted value and transforms each emission to an Observable that is built up during stream creation.
The thumb rule here is use switchMap when your inner stream depends on the source stream values and use switchMapTo when it doesn't.
But what if I don't care for the emission value but I do care for the emission time?
Meaning I'd like the inner Observable to be evaluated upon source Observable emission.
The obvious thing here is to use switchMap(() => Observable) but it just doesn't feel right, because of the thumb rule I mentioned before.
Example with switchMapTo (bad):
const predefinedKey = 'key';
//This need to be initialized
const obj = {};
function getObservable(key){
return Rx.Observable.of(obj[key]);
}
//This is initialization stream
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
//Would like to access the object only after initialization
const result = onInit.switchMapTo(getObservable(predefinedKey));
//Expect to see 1 in output but see 'undefined' because switchMapTo evaluated before the object is initialized
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Example with switchMap (good):
const predefinedKey = 'key';
//This need to be initialized
const obj = {};
function getObservable(key){
return Rx.Observable.of(obj[key]);
}
//This is initialization stream
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
//Would like to access the object only after initialization
const result = onInit.switchMap(() => getObservable(predefinedKey));
//Expect to see 1 in output
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
The examples are very artificial but they describe the situation pretty well.
What is the right approach here? Any other Observable function I can use for delayed execution?
Based on your example, you can use switchMapTo in combination with Observable.defer:
const predefinedKey = 'key';
const obj = {};
function getObservable(key){
return Rx.Observable.defer(() => Rx.Observable.of(obj[key]));
}
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
const result = onInit.switchMapTo(getObservable(predefinedKey));
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Instead of deferring in getObservable, you could also defer in the switchMapTo call:
const result = onInit.switchMapTo(Rx.Observable.defer(() => getObservable(predefinedKey)));
This will just depend on the situation. That said, I also don't think there's anything wrong with using switchMap and personally, I'd probably do that instead of deferring (which is useful in other situations).
I'm having some trouble making use of the Config.skip property inside of my graphql() wrapper.
The intent is for the query to be fired with an argument of currentGoalID, only after a user has selected an item from the drop-down (passing the associated currentGoalID) , and the (Redux) state has been updated with a value for currentGoalID.
Otherwise, I expect (as per Apollo documentation) that:
... your child component doesn’t get a data prop at all, and the options or props methods are not called.
In this case though, it seems that my skip property is being ignored based upon the absence of a value for currentGoalID, and the option is being called because the webpack compiler/linter throws on line 51, props is not defined...
I successfully console.log the value of currentGoalID without the graphql()
wrapper. Any idea why config.skip isn't working? Also wish to be advised on the proper use of this in graphql() function call. I've excluded it here, but am unsure of the context, thanks.
class CurrentGoal extends Component {
constructor(props) {
super(props)
}
render (){
console.log(this.props.currentGoalID);
return( <p>Current Goal: {null}</p>
)
}
}
const mapStateToProps = (state, props) => {
return {
currentGoal: state.goals.currentGoal,
currentGoalID: state.goals.currentGoalID,
currentGoalSteps: state.goals.currentGoalSteps
}
}
const FetchGoalDocByID = gql `
query root($varID:String) {
goalDocsByID(id:$varID) {
goal
}
}`;
const CurrentGoalWithState = connect(mapStateToProps)(CurrentGoal);
const CurrentGoalWithData = graphql(FetchGoalDocByID, {
skip: (props) => !props.currentGoalID,
options: {variables: {varID: props.currentGoalID}}
})(CurrentGoalWithState);
// export default CurrentGoalWithState
export default CurrentGoalWithData
See the answer here: https://stackoverflow.com/a/47943253/763231
connect must be the last decorator executed, after graphql, in order for graphql to include the props from Redux.