Get access to atom values outside RecoilRoot - javascript

In my project, i'm using "react-three/fiber" which is required to store all content inside "Canvas".
Also, for storing some states and share it with other modules in "Canvas" i'm using recoil's atom.
So, inside "RecoilRoot" everything works great, BUT i need to show data from atom in the "UI" component.
I can't put "RecoilRoot" out of "Canvas", so i tried to add a second "RecoilRoot". It kinda does the trick (now i can use "useRecoilValue" in "UI"), but it shows only default state of atom...
Here is how my "App.js" looks like:
function App() {
return (
<Suspense fallback={<Loading />}>
{/*Second RecoilRoot, outside the Canvas*/}
<RecoilRoot>
<Canvas shadows>
<GameSettingsProvider>
{/*Main RecoilRoot with data*/}
<RecoilRoot>
<ObjectShow />
{/*Main RecoilRoot with data*/}
</RecoilRoot>
</GameSettingsProvider>
</Canvas>
<UI />
{/*Second RecoilRoot, outside the Canvas*/}
</RecoilRoot>
</Suspense>
);
}
export default App;
Atom with data:
import {atom} from "recoil";
export const scoreState = atom({
key: "score", // unique ID (with respect to other atoms/selectors)
default: 0, // default value (aka initial value)
});
UI module:
import React from 'react'
import { useRecoilValue } from 'recoil'
import { scoreState } from './gameState'
function UI() {
const score = useRecoilValue(scoreState);
return (
<div className='score-title'>
<h1 className='score-title__text'>
Score: {score}
</h1>
</div>
)
}
export default UI
So, how can i use atom values outside "RecoilRoot" or, at least, share from nested one?

There are 2 different renders DOM and Canvas.
Couldn't find any workarounds to transfer states via Recoil, so i used Zustand state manager for score state.
It can transfer states even between renders!
Work like a charm.

Related

Not able to render images in Gatsby from .yml file [duplicate]

I'm trying to display couple of images from array of objects containing images urls. I'm aware that StaticImage has to accept local variables as props values. These two variables for image urls are both local.
Why is this not working and is there a workaround?
import React from 'react';
import { StaticImage } from 'gatsby-plugin-image';
const TestPage = (props) => {
const itemData = [
{
img: 'https://images.unsplash.com/photo-1549388604-817d15aa0110?w=161&fit=crop',
title: 'Bed',
},
{
img: 'https://images.unsplash.com/photo-1563298723-dcfebaa392e3?w=161&fit=crop',
title: 'Kitchen',
},
];
const testSrc = 'https://images.unsplash.com/photo-1523413651479-597eb2da0ad6?w=161&fit=crop';
const testSrc2 = itemData[0].img;
return (
<>
<StaticImage src={testSrc} alt="works" />
<StaticImage src={testSrc2} alt="doesn't" />
</>
)
}
export default TestPage;
As you said, there's a restriction in the component:
Restrictions on using StaticImage
The images are loaded and processed at build time, so there are
restrictions on how you pass props to the component. The values need
to be statically-analyzed at build time, which means you can’t pass
them as props from outside the component, or use the results of
function calls, for example. You can either use static values, or
variables within the component’s local scope. See the following
examples:
In your case, a plain assignation works but an object assignation doesn't. Change it to:
import React from 'react';
import { StaticImage } from 'gatsby-plugin-image';
const TestPage = (props) => {
const testSrc = 'https://images.unsplash.com/photo-1523413651479-597eb2da0ad6?w=161&fit=crop';
const testSrc2 = 'https://images.unsplash.com/photo-1563298723-dcfebaa392e3?w=161&fit=crop';
return (
<>
<StaticImage src={testSrc} alt="Bed" />
<StaticImage src={testSrc2} alt="Kitchen" />
</>
)
}
export default TestPage;

ReactJS - setting state in an arrow function

I'm trying to figure out how to set the initial state in my React app inside an arrow function. I've found the example here: https://reactjs.org/docs/hooks-state.html but it's not helping me a lot. I want to put tempOrders and cols into the state so my other components have access to them and can change them.
Here is my code:
// creating tempOrders array and cols array above this
const App = () => {
const [orders, setOrders] = useState(tempOrders);
const [columns, setColumns] = useState(cols);
return (
<div className={'App'}>
<Schedule
orders={orders}
setOrders={setOrders}
columns={columns}
setColumns={setColumns}
/>
</div>
);
};
export default App;
Now my other related question is if I don't pass in those 4 variables/functions into Schedule, ESLint complains to me about them being unused variables in the 2 const lines above. I wouldn't think I would need to pass them in because that is the whole point of state, you just have access to them without needing to pass them around.
You should always keep the state at the top-level component where it needs to be accessed. In this case you should define the state in the Schedule-Component since it's not used anywhere else.
If you have a more complex hierachy of components and want to create a shared state (or make a state globally accessible) I would suggest following thump rule:
For small to medium sized apps use the context-API with the useContext-hook (https://reactjs.org/docs/hooks-reference.html#usecontext). It's fairly enough for most cases.
For large apps use redux. Redux needs a lot of boilerplate and adds complexity to your app (especially with typescript), which is often not required for smaller apps. Keep in mind that redux is not a replacement for thecontext-API. They work well in conjunction and can/should be used together.
EDIT
Simple example for useContext:
ScheduleContext.js
import React from "react";
export const ScheduleContext = React.createContext();
App.jsx
import {ScheduleContext} from "./ScheduleContext";
const App = () => {
const [orders, setOrders] = useState(tempOrders);
const [columns, setColumns] = useState(cols);
const contextValue = {orders, setOrders, columsn, setColumns};
return (
<div className={'App'}>
<ScheduleContext.Provider value={contextValue}>
<Schedule/>
</ScheduleContext.Provider>
</div>
);
};
export default App;
You can now use the context in any component which is a child of the <ScheduleContext.Provider>.
Schedule.jsx
import React, {useContext} from "react";
import {ScheduleContext} from "./ScheduleContext";
const Schedule = () => {
const {orders, setOrders, columsn, setColumns} = useContext(ScheduleContext);
// now you can use it like
console.log(orders)
return (...)
}
Note that you could als provide the context inside the <Schedule>-component instead of <App>.
I wrote this from my head, but it should work. At least you should get the idea.
it seems you want the child component "Schedule" have to change the father's state...... is correct?
so you can try to write like this example:
import React, {useState} from 'react';
import './App.css';
function Test(props){
const{setCount,count}=props
return(
<div>
<h1>hello</h1>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
function App() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<Test
setCount={setCount}
count={count}
/>
{count}
</div>
);
}
export default App;
https://repl.it/#matteo1976/ImperfectYawningQuotes
Where my Test would work as your Schedule

How to set default values on imported components in REACT

Lets say I am using a <FormattedNumber > that I am importing from react-intl
It has a property called minimumSignificantDigits, so that if I set it all my numbers look awesome like 1.00... great when you are working with currencies.. so I can use it like this:
<FormattedNumber minimumSignificantDigits={3} value={somevar}>
I have about 100 of these on the page and I don't want to keep setting this minimumSignificantDigits property on every single on of them, and then when the client changes his/her mind I have to update all of them.
Is there any way that I can set/override some default properties on that component when I import it.
Obviously yes, make a wrapper around <FormattedNumber>
// TreeCharFormattedNumber.jsx
export default TreeCharFormattedNumber = ({ value }) => (
<FormattedNumber minimumSignificantDigits={3} value={value}>>
);
// YourComponent.jsx
import TreeCharFormattedNumber from "./TreeCharFormattedNumber";
...
<div>
<TreeCharFormattedNumber value={myAwsomeValue} />
</div>
...
You can also put TreeCharFormattedNumber in the same file leaving export default
Wrap the imported component with another.
In this example, the default value for minimumSignificantDigits would be 3 with any other props passed through as is. (This allows you to also override your default on a per component basis if required)
function FormattedNumberWithDefault(props) {
return (
<FormattedNumber minimumSignificantDigits={3} {...props}>
)
}
Wrap it with your own component:
export const MyFormattedNumber = (props) => (
<FormattedNumber minimumSignificantDigits={3} {...props}>
);
Now you can import it whenever it's needed, and everything you'll pass to MyFormattedNumber will be passed to the wrapped FormattedNumber:
<MyFormattedNumber value={3} />
You can easily override the default if you pass the property minimumSignificantDigits, because spreading the props can replace the default prop as well:
<MyFormattedNumber minimumSignificantDigits={15} value={somevar}>
I have found that the following also works:
import React from 'react'
import {FormattedNumber} from 'react-intl'
import {Link} from 'react-router-dom'
FormattedNumber.defaultProps = {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}

The prop `store.subscribe` is marked as required

I am trying to connect a component to the redux store, but am receiving:
Warning: Failed prop type: The prop 'store.subscribe' is marked as required inConnect(StoreLocation), but its value is 'undefined'.
I have been using redux with this project for awhile now without issue, but this component is erroring out for some reason and I'm clueless as to why at this point :(
The store populates a collection of stores (brick and mortar locations with addresses, phone numbers, etc used for shipping selections) within DeliverySection.js.
Then each StoreLocation.js component will allow the user to view it's info, select it, etc. It's bare bones right now as I am seeing the error even at this basic point. If I switch the export default connect()(StoreLocation) statement with export default StoreLocation it works without issue.
Any ideas?
DeliverySection.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
// Components
import Loader from '../../utils/Loader'
import StoreLocation from './StoreLocation'
// Stote
import { getAllStores } from '../../../store/actions/storeLocation'
import { REACT_APP_SITE_KEY } from '../../../shared/vars'
// CSS
import '../../../css/delivery.css'
class DeliverySection extends Component {
componentDidMount(){
this.props.getAllStores(REACT_APP_SITE_KEY);
}
render() {
const { stores, isLoading } = this.props
return (
<div>
<div className="delivery-heading">
<h2>Choose a store near you:</h2>
<button className="btn btn--red btn--heading" name="ship-to-address">Ship To An Address</button>
</div>
<div>
{isLoading ? (
<Loader />
) : (
!isLoading && !!stores ? (
stores.map((store, i) => <StoreLocation key={i} store={store} />)
) : (
<div>
There are no store locations to deliver to.<br />
Ship to an address!
</div>
)
)}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
stores: state.storeLocation.stores,
isLoading: state.storeLocation.isLoading
}
}
export default connect(mapStateToProps, { getAllStores })(DeliverySection)
StoreLocation.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { setDelivery } from '../../../store/actions/checkout'
class StoreLocation extends Component {
render() {
const { store } = this.props
return (
<div className="store-location">
<div className="store-row">
<div className="store-col"><div className="store-title">{store.title}</div></div>
<div className="store-col">
{store.address}
{store.formatted_location &&
<div>{store.formatted_location}</div>
}
</div>
<div className="store-col">
<button className="btn select-store" onClick={() => this.props.setDelivery(store)}>Ship to this store<span className="icon-checkmark"></span></button>
</div>
</div>
<div className="store-row">
<div className="store-col">
<div className="ajax-message" data-hbs-id="postal-{id}"></div>
<input type="hidden" id={`postal-${store.id}`} value={store.postal} />
<div className="see-map"><span className="icon-location"></span>See on map</div>
</div>
<div className="store-col">{store.description}</div>
<div className="store-col"></div>
</div>
{store.phone &&
<div className="store-row">
<div className="store-col"></div>
<div className="store-col">{store.phone}</div>
<div className="store-col"></div>
</div>
}
</div>
)
}
}
export default connect(null, { setDelivery })(StoreLocation)
// export default StoreLocation
It's because you are using store as your prop name. You are overwriting the prop react-redux passes through the HOC. Since, the object you pass for store does not have a subscribe method, you get this error.
If you change the name of your prop, you'll be in good shape again.
After doing a quick Google I came across this post here.
That problem, which is similar to yours, was based on the way the store was exported. Have a look at that and see if gets you going in the right direction. I can't comment without seeing your store export code.
On a personal preference note I would use something other than 'store' as the variable for each instance in your map of stores. Since you are using Redux it could get semantically confusing whether you are referring to the Redux store or an instance of a store object.
I think it's fine that you are having StoreLocation handle the setting of delivery. I'm a big fan of breaking things down into smaller components.
Finally, just because I happen to notice it, you have a misspelling in DeliverySection. Line 8 reads //Stote. I'm guessing you meant //Store.
Apologies in advance as I think this should go under the comment section, but the code you pasted looks alright. You say disconnecting the StoreLocation component fixes things. Is there a reason you want to connect that component? You're not mapping any state to props or using dispatch in that component.
Otherwise, make sure that you're correctly initializing the store with the reducers you need and check that the modules you're using are imported properly - especially the ones you are passing to the connect function (getAllStores).

Global state in React Native

I am developing a React Native application.
I want to save the user id of the person who is logged in and then check if the user is logged in in every single component.
So what I am looking for is something like cookies, sessions or global states.
I have read that I should use Redux, but this seems to be overly complicated and it is very difficult to make it work with react-navigation. It forces me to define actions and reducers for almost everything although the only thing I want is to be able to access a single global state/variable in all components.
Are there any alternatives or should I really re-structure my entire app to use Redux?
I usually create a global.js containing:
module.exports = {
screen1: null,
};
And get the value of the state on the screen
import GLOBAL from './global.js'
constructor() {
GLOBAL.screen1 = this;
}
Now you can use it anywhere like so:
GLOBAL.screen1.setState({
var: value
});
Update since React 16.8.0 (February 6, 2019) introduce Hooks.
it is not mandatory to use external library like Mobx or Redux. (Before Hook was introduce I used both of this state management solutions)
you can create global state just with 10 line Source
import React, {createContext, useContext, useReducer} from 'react';
export const StateContext = createContext();
export const StateProvider = ({reducer, initialState, children}) =>(
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
extend your app with global state:
import { StateProvider } from '../state';
const App = () => {
const initialState = {
theme: { primary: 'green' }
};
const reducer = (state, action) => {
switch (action.type) {
case 'changeTheme':
return {
...state,
theme: action.newTheme
};
default:
return state;
}
};
return (
<StateProvider initialState={initialState} reducer={reducer}>
// App content ...
</StateProvider>
);
}
For details explanation I recommend to read this wonderful medium
There are some alternatives to Redux in terms of state management. I would recommend you to look at Jumpsuit and Mobx. However do not expect them to be easier than Redux. State management is mostly a magical thing and most of the gizmo happens behind the scenes.
But anyways if you feel that you need some global state management, it worths your time to master one of the solutions no matter Redux or Mobx or etc. I would not recommend using AsyncStorage or anything hacky for this purpose.
I usually do globals like this:
I creat an globals.js
module.exports = {
USERNAME: '',
};
Something like that to store the username then you just need to import :
GLOBAL = require('./globals');
And if you wanna store the Data, lets say you want to save the username just do :
var username = 'test';
GLOBAL.USERNAME = username;
And there you go , you just need to import GLOBAL on the pages you want and use it, just use if (GLOBAL.username == 'teste').
If you are new to react (as me) and got confused by the first answer.
First, use a component Class
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
walk: true
};
GLOBAL.screen1 = this;
}
render() {
return (
<NavigationContainer>
<Stack.Navigator>
{this.state.walk ? (
<>
<Stack.Screen name="WalkThrough" component={WalkThroughScreen} />
</>
) : (
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
<StatusBar style="auto" />
</NavigationContainer>
)
}
Then you can do in any other component (My components are on /components, global is on root):
import GLOBAL from '../global.js'
GLOBAL.screen1.setState({walk:false})
There appears to be a GLOBAL object. If set in app.js as GLOBAL.user = user, it appears to be available in other components, such as the drawer navigation.
this is an old question but I have a solution that helps me.
To accomplish this, I use what is called a GlobalProvider, essentially provides global data to all components. A lot of this code was learned through YouTube Tutorials so I can not take credit for the ideas. Here is the code,
export const GlobalContext = createContext({});
const GlobalProvider = ({children}) => {
//authInitialState can be whatever you want, ex: {rand: {}, rand2: null}
const [authState, authDispatch] = useReducer(auth, authInitialState);
return (
<GlobalContext.Provider
value={{authState, authDispatch}}>
{children}
</GlobalContext.Provider>
);
};
export default GlobalProvider;
Then you would simply wrap your entire application (usually app.js) with GlobalProvider as so. Ignore my AppNavContainer, that just contains code that routes my pages.
import GlobalProvider from "./src/Context/Provider";
const App: () => Node = () => {
return (
<GlobalProvider>
<AppNavContainer/>
</GlobalProvider>
);
};
From here on you are able to change the authState with a reducer of some sort, I will not provide that code since it is huge, but look at Soullivaneuh's example on the reducer above.
NOW to the good part, of how to access your state. It is simple, in any component you wish, simply follow a similar structure like this. Notice that I have {data} as it will allow you to see the state.
const {
authState: {data},
} = useContext(GlobalContext);
console.log("Data:", data)
If anyone can correct me where I went wrong, I'd appreciate it as well.
Same as #Brunaine suggested, but I import it only in the App.js and can use it in all the screens.

Categories