I am having trouble passing the values of state from one file to another. I need to do this because when I get to pulling information from the DB, it will need to display more details about these values.
I have a FlatList that when is clicked it sets the value of the item clicked a state value. But it should also navigate to a new screen, while also passing these values to that new screen to be pulled from the DB.
Here is my function that sets the state and calls the function to pass values to the new screen.
_onSectionListPress = (id) {
this.setState({ jobId: id}, () => this._showJobDetail());
};
Here is my function to navigate to the new screen. But this is where I get stuck on how to pass the values.
_showJobDetail = () => {
this.props.navigation.navigate("JobDescriptions", this.state.stakeholderID, this.state.jobId)
};
I'm using react-navigation to try and do this.
Sorry for asking what seems simple in such a complicated way. I'm still pretty new to react-native and I'm not sure how to do this.
you should pass the state variable like this
this.props.navigation.navigate('JobDescriptions', {
stakeholderID: this.state.stakeholderID,
jobId: this.state.jobId,
});
And call those state variable like this
this.props.navigation.getParam("stakeholderID", "noId")
this.props.navigation.navigate('JobDescriptions', {stakeholderID: this.state.stakeholderID,})
in the new screen you can get value like this
const { navigation } = this.props;
const JobDescriptions= navigation.getParam('JobDescriptions', null);
You should lift the state. One way to do this is to establish a store to keep track of the data. There are many examples of how to do this, including one I described here and off-the-shelf packages like Redux.
Related
I have recently started a project in a team of three people. We've been learning some React for that, and when I explained to a friend what I was trying to do, he advised me to try it with Redux, but I'm not quite sure how I'm supposed to proceed.
My goal is to generate a code in an Arrival.js page, depending on a form that the customer filled, based on his first name and the date. I have the right function to generate that code. Now when the customer submits the form, a pop-up shows up, telling him "here's your code: CODE1245" for example. The thing is that this pop-up is a component located in another file named PopUpArrival.js and I can't manage to transfer the code generated in Arrival.js with the form's information.
So I tried to set up a store with reducers and actions, but I can't manage to find any actions that would fit my goals. Am I overthinking it by trying to use Redux? Or is there an easy way to do so? It's been three days and I'm quite lost and demotivated to be honest. Thanks if anyone took the time to read me and I'd be even more grateful if anyone shows up with a solution
If your components have a
parent-child relationship, then it would be easy to send the code as a prop to the child.
Sibling relationship, then What is the most simplest method of updating component's state from another component? Lets say components are siblings?
Not related at all, there are a lot of ways like cookies, local storage, context, redux, etc.
I guess using redux only for this purpose might be overkill. You can look at contexts in that case.
I think Redux maybe an overkill here. You can get this done without redux from how it looks. If you can consider the following flow.
Your Arrival.js will maintain state for the first name and the date. Your PopUpArrival.js should be a component in itself, which takes whatever text you want to show in popup as a prop. Based on when you want to trigger the popup logic, you should display the PopUpArrival component. I have tried to roughly write some code below which does the same.
const PopUpArrival = (props) => {
const { textToShow } = props;
return(
<div>{textToShow}</div>;
);
}
const Arrival = () => {
// maintains state for both name and date
const [showPopup, setPopupDisplay] = useState(false);
const togglePopup = () => {
setPopupDisplay((currentDisplay) => !currentDisplay);
}
const getTextToShow = () => {
return `${name}-{date}`; // whatever logic you have
}
return(
<>
<button onClick={togglePopup}>
Submit
</button>
{
showPopup && <PopUpArrival textToShow={getTextToShow()}/>
}
</>
)
}
I've just started using Recoil on a new project and I'm not sure if there is a better way to accomplish this.
My app is an interface to basically edit a JSON file containing an array of objects. It reads the file in, groups the objects based on a specific property into tabs, and then a user can navigate the tabs, see the few hundred values per tab, make changes and then save the changes.
I'm using recoil because it allows me to access the state of each input from anywhere in my app, which makes saving much easier - in theory...
In order to generate State for each object in the JSON file, I've created an component that returns null and I map over the initial array, create the component, which creates Recoil state using an AtomFamily, and then also saves the ID to another piece of Recoil state so I can keep a list of everything.
Question 1 Is these a better way to do this? The null component doesn't feel right, but storing the whole array in a single piece of state causes a re-render of everything on every keypress.
To Save the data, I have a button which calls a function. That function just needs to get the ID's, loop through them, get the state of each one, and push them into an Array. I've done this with a Selector too, but the issue is that I can't call getRecoilValue from a function because of the Rules of Hooks - but if I make the value available to the parent component, it again slows everything right down.
Question 2 I'm pretty sure I'm missing the right way to think about storing state and using hooks, but I haven't found any samples for this particular use case - needing to generate the state up front, and then accessing it all again on Save. Any guidance?
Question 1
Get accustomed to null-rendering components, you almost can't avoid them with Recoil and, more in general, this hooks-first React world 😉
About the useRecoilValue inside a function: you're right, you should leverage useRecoilCallback for that kind of task. With useRecoilCallback you have a central point where you can get and set whatever you want at once. Take a look at this working CodeSandbox where I tried to replicate (the most minimal way) your use-case. The SaveData component (a dedicated component is not necessary, you could just expose the Recoil callback without creating an ad-hoc component) is the following
const SaveData = () => {
const saveData = useRecoilCallback(({ snapshot }) => async () => {
const ids = await snapshot.getPromise(carIds);
for (const carId of ids) {
const car = await snapshot.getPromise(cars(carId));
const carIndex = db.findIndex(({ id }) => id === carId);
db[carIndex] = car;
}
console.log("Data saved, new `db` is");
console.log(JSON.stringify(db, null, 2));
});
return <button onClick={saveData}>Save data</button>;
};
as you can see:
it retrieves all the ids through const ids = await snapshot.getPromise(carIds);
it uses the ids to retrieve all the cars from the atom family const car = await snapshot.getPromise(cars(carId));
All of that in a central point, without hooks and without subscribing the component to atoms updates.
Question 2
There are a few approaches for your use case:
creating empty atoms when the app starts, updating them, and saving them in the end. It's what my CodeSandbox does
doing the same but initializing the atoms through RecoilRoot' initialState prop
being updated by Recoil about every atom change. This is possible with useRecoilTransactionObserver but please, note that it's currently marked as unstable. A new way to do the same will be available soon (I guess) but at the moment it's the only solution
The latter is the "smarter" approach but it really depends on your use case, it's up to you to think if you really want to update the JSON at every atom' update 😉
I hope it helps, let me know if I missed something 😊
I'm trying to create a stepper form
I store my steps in an array of json with a proprety component ({typeOfComponent, component, key})
It works wells, but:
Everytime i slice my array, like when i move up/down a step or add a new step between two steps.
I lose the states inside my component.
I tried to use memo, i don't understand why it's only when an item position my composent is recreate. Is it possible like a pointer in C to store only his "adress"
the code sandbox exemple =>
https://codesandbox.io/s/infallible-maxwell-zkwbm?file=/src/App.js
In my real projet, the button ADD is a button for chosing the new step type
Is there any solution for manipulates my steps without losing the user data inside ?
Thanks for your help
React is re-mounting the components inside of this every re-render probably due to a variety of reasons. I couldn't get it to work as is, but by lifting the state up from your components, it will work.
You'd likely need to lift the state up anyway because the data isn't where you need it to be to make any use of your form when the user is done with it.
In order to lift the state up, I added the current value to the steps array:
function addNext(step, index) {
componentKey++;
setSteps(prevState => {
let newState = [...prevState];
step = 1;
newState.splice(index + 1, 0, {
stepNumber: step,
component: getStepContent(step, componentKey),
value: getDefaultValue(step),
key: componentKey
});
return newState;
});
}
I also made sure your getStepContent just returned the component rather than a node so you can render it like this:
<step.component
value={step.value}
onChange={handleChange}
data-index={i}
/>
There are definitely a lot of ways to optimize this if you start running into performance issues, of course.
https://codesandbox.io/s/beautiful-river-2jltr?file=/src/App.js
being new to redux, I came across the following challenge: I have a filter-list, which must be updated both after the first load of a component, and every time someone changes a dropdown in the top navbar. The filter-list is dependent on another dropdown which is part of my state. A pseudocode is below [1].
I could theoretically duplicate my action dispatch in both Components (the main one and the navbar), but this is bad design of course.
This brings me to the question: Where does one put redux dispatch calls which must access the state themselves? Would this live in the action-file which would then need to access the state itself (which might not be good design on its own)?
Thanks!
[1]
function getDrillOptions() {
let data = props.data; // <- is part of redux' store
let countries = props.selectedFilters.countries; //<- part of redux' store
let regions = getRegionForCountry(countries);
let selectable = new Set();
Object.keys(data[regions][countries]).map((weeknum) => {
data[regions][countries][weeknum].map((row) => {
selectable.add(row[event.target.value]);
});
});
dispatch(populateFilters({filter: 'mainDrillOptions', value: [...selectable]})); // <- sets state
}
I know Redux solves this but I came up with an idea.
Imagine I have an app that gets some JSON on start. Based on this JSON I'm setting up the environment, so let's assume the app starts and it downloads an array of list items.
Of course as I'm not using Redux (the app itself is quite simple and Redux feels like a huge overkill here) if I want to use these list items outside of my component I have to pass them down as props and then pass them as props again as deep as I want to use them.
Why can't I do something like this:
fetch(listItems)
.then(response => response.json())
.then(json => {
window.consts = json.list;
This way I can access my list anywhere in my app and even outside of React. Is it considered an anti-pattern? Of course the list items WON'T be changed EVER, so there is no interaction or change of state.
What I usually do when I have some static (but requested via API) data is a little service that acts kind like a global but is under a regular import:
// get-timezones.js
import { get } from '../services/request'
let fetching = false
let timez = null
export default () => {
// if we already got timezones, return it
if (timez) {
return new Promise((resolve) => resolve(timez))
}
// if we already fired a request, return its promise
if (fetching) {
return fetching
}
// first run, return request promise
// and populate timezones for caching
fetching = get('timezones').then((data) => {
timez = data
return timez
})
return fetching
}
And then in the view react component:
// some-view.js
getTimezones().then((timezones) => {
this.setState({ timezones })
})
This works in a way it will always return a promise but the first time it is called it will do the request to the API and get the data. Subsequent requests will use a cached variable (kinda like a global).
Your approach may have a few issues:
If react renders before this window.consts is populated you won't
be able to access it, react won't know it should re-render.
You seem to be doing this request even when the data won't be used.
The only downside of my approach is setting state asynchronously, it may lead to errors if the component is not mounted anymore.
From the React point of view:
You can pass the list from top level via Context and you can see docs here.
Sample of using it is simple and exists in many libraries, such as Material UI components using it to inject theme across all components.
From engineering concept of everything is a trade of:
If you feel that it's gonna take so much time, and you are not going to change it ever, so keep it simple, set it to window and document it. (For your self to not forget it and letting other people know why you did this.)
If you're absolutely certain they won't ever change, I think it's quite ok to store them in a global, especially if you need to access the data outside of React. You may want to use a different name, maybe something like "appNameConfig"..
Otherwise, React has a feature called Context, which can also be used for "deep provision" - Reference