Store result from api call React native - react-native

How can I store the recipe name from this api call into a state variable or some other variable to be used further down in the render
{
this.props.recipeList.recipeList.filter((recipe) => recipe.recipeName === search).map(recipe => {
return <Recipe
name={recipe.recipeName}
numberOfServings={recipe.numberOfServings}
key={'recipe-' + recipe.recipeId}
/>
})
}

As this is performed within render, avoid modifying the component state.
I would recommend declaring a variable at the top of your render method and then assigning it in your map call. This, of course, assumes that filter will only ever return a single result.
Something like:
render() {
let name;
....
{
this.props.recipeList.recipeList.filter((recipe) => recipe.recipeName === search).map(recipe => {
name = recipe.recipeName; // this bit
return <Recipe
name={recipe.recipeName}
numberOfServings={recipe.numberOfServings}
key={'recipe-' + recipe.recipeId}
/>
})
}
....

Related

How exactly do I update FlatList's data using Apollo Client's fetchMore method?

My GraphQL backend returns data in the form:
{
data: {
feed: {
A: [...],
B: [...]
}
}
}
Where my frontend merges A and B using a sort on some field present within the data, and then uses React Native's FlatList to display them as a single array. I'm using Apollo client to query my backend like so:
const { fetchMore, loading, data, error } = useQuery(fetchQuery, {variables: fetchArgs});
if (loading) {...}
if (error) {...}
let A = [];
let B = [];
if (data!.feed.A) A = data!.feed.A
if (data!.feed.B) B = data!.feed.B
let feedData = sortedMerge(A,B);
return (
<FlatList
data={feedData}
...
onEndReached{
//update fetchArgs
fetchMore({
variables: fetchArgs,
updateQuery: // Not sure if I need to do anything here?
}).then(result => {
// Maybe this is where I update?
})
}
)
However, I can't seem to figure out how or where to get the new data, say A' and B' merged and concatenated with feedData so that my FlatList can update as efficiently as possible?
I guess I'm having trouble because I can't just directly update the list and need to do a little bit of preprocessing, but no matter where I update feedData, either in the then block after fetchMore or outside of it, the FlatList never seems to update.
Try putting your merge logic in a useMemo:
const { fetchMore, loading, data, error } = useQuery(fetchQuery, {variables: fetchArgs});
const feedData = useMemo(() => {
if (data) {
const { A, B } = data.feed;
return sortedMerge(A,B);
} else return [];
},[data]);
if (loading) {...}
if (error) {...}
return (
<FlatList
data={feedData}
...
onEndReached = {() => fetchMore({ variables: fetchArgs })}
/>
)
Executing fetchMore should cause data to be updated which will trigger the useMemo and update your feedData variable.
However you'll still need to merge the paginated results into the client-side cache.

How can I access to each element inside of a grid element with Cypress?

I have a Grid component which includes 24 divs and inside of each div I need to take the value.
This value actually arrives in <p>, so which is the best way to do this?
Below is the app image. I would appreciate an example.
You could probably do something like storing the data in an object or array outside of the Cypress chain. Without a code example, here's my best guess.
cy.get('grid').within(() => { // cy.within() searches only within yielded element
const data = {}; // create data object
cy.get('div').each(($div, index) => { // cy.each() allows us to iterate through yielded elements
data[index] = $div.text() // or possibly some other JQuery command to get the value
// additionally, could go without the index at all and make `data` an array.
}).then(() => {
// whatever needs to be done with `data`, wrapped in `then` to make sure data is populated correctly.
});
});
You can add use each for this to loop through the elements and then do further operations:
cy.get('.chakra-stack')
.find('p')
.each(($ele) => {
cy.log($ele.text().trim()) //Logs all the texts one by one
})
Just add the p selector to your cy.get() command
cy.get('div.chakra-stack p') // access <p> within <div>
.each($el => {
cy.wrap($el).invoke('text')
.then(text => {
...
})
})
To get the value before the text
cy.get('div.chakra-stack p') // access <p> within <div>
.each($el => {
cy.wrap($el)
.prev() // element with value
.invoke('text')
.then(value => {
...
})
})
Accessing values by text label like this
const values = {}
cy.get('div.chakra-stack p')
.each($el => {
const frase = $el.text()
cy.wrap($el).prev().invoke('text')
.then(value => values[frase] = +value)
})
.then(() => {
// values = {'shield': 1, 'session': 2, ...}
})

Export child components and use individually

I have a case where I need to export two child components and use individually.
Much Desired outcome (Extremely simplified):
Controls.js:
const Controller = ( props ) => {
const ControlBoxes = () => {
return(<Button>Move around!</Button>)
}
const MoveableBox = () => {
return(<View>I will be moved! </View>)
}
return {ControlBoxes, MoveableBox}
}
export default Builder
Canvas.js:
import Controller from './controls'
const boxScaleMove = boxes.map((box, index) => {
return (
<Bulilder.MoveableBox key={box.id} box={box}/>
)
}
const boxController = boxes.map((box, index) => {
return (
<Bulilder.ControlBoxes key={box.id} box={box}/>
)
}
return (
...
{boxController}
...
...
{boxScaleMove}
...
)
Any idea how I can achieve this or am I missing something fundamental? The main issue is that I want to avoid resorting to useContext (due to performance reasons in the case of a lot of boxes rendered) and be able to share variables and states between MoveableBox-component and ControlBoxes-component via Controller -parent.
Any help would be greatly appreciated.
You could use the compound component and use a lower level context to avoid re-rendering of the whole tree and share states across your components that way, below I would ilustrate a basic example of how that would work.
const RandomContext = createContext();
export default function Controller({children, ...rest}) {
const [randomState, setRandom] = useState(0);
return (
<RandomContext.Provider value={{ randomState, setRandom }}>
<div {...rest}>{children}</div>
</RandomContext.Provider>
);
}
Controller.ControlBoxes = function (props) {
const { setRnadom } = useContext(RandomContext);
return (
<Button onClick={() => setRandom(2)} {...props}>Move around!</Button>
);
};
Controller.MoveableBox = function (props) {
const { randomState } = useContext(RandomContext);
return randomState ? <View {...props}>I will be moved!</View> : null;
};
And you would use it as:
<Controller>
<Controller.ControlBoxes />
<Controller.MoveableBox />
<Controller>
In the compound components pattern we are leveraging the fact that in javascript when you declare a function you create a function/object combo. Therefor Controller function is both a function and an object, so we can assign properties the the object part of that combo, properties which are in our case ControlBoxes and MoveableBox which are functions themselves.
NOTE you should probably assign named function the the properties of that object, it's easier to debug if the case needed.
Example.Function = function ExampleFunction(props) {
return "Example";
};

Mobx Observable not triggering changes

I've been working to implement MobX into one of my classes, and I believe I'm close to have it working but I'd really appreciate if someone can point out where I'm going wrong here.
Essentially when the refreshJobs() function runs, I'd like the render() function to execute again. From my understanding, if I updated the observable object jobs, the computed functions (renderSubmittedJobs(), renderInProgressJobs()) would run again producing new values, then the render function would run again since those values have been updated.
However, what happens with this code is that it updates this.jobs (wrapped in an action), but neither of the computed functions execute - and I believe that is why render isn't ran again either.
Does anyone know what might be causing this issue? I really appreciate any direction with this.
#observer
export default class Jobs extends React.Component<ScreenProps<>> {
#observable jobs = {};
#computed get renderInProgressJobs() {
inProgressJobs = [];
for (key in this.jobs) {
if (jobs[key].status === "in progress") {
inProgressJobs.push(this.jobs[key]);
}
}
return this.renderJobComponents(inProgressJobs);
}
#computed get renderSubmittedJobs() {
submittedJobs = [];
for (key in this.jobs) {
console.log(key)
if (this.jobs[key].status !== "in progress") {
submittedJobs.push(this.jobs[key]);
}
}
return this.renderJobComponents(submittedJobs);
}
renderJobComponents(jobList: Array) {
return jobList.map((jobInfo, key) => {
return (
...
);
});
}
#observer
async refreshJobs() {
jobs = await grabClientJobs(refresh=true);
await runInAction("Updating Jobs", async () => {
this.jobs = jobs;
});
}
#observer
async componentWillMount() {
jobs = await grabClientJobs();
runInAction("Updating Jobs", async () => {
this.jobs = jobs;
});
}
#observer
render(): React.Node {
console.log('in jobs now');
return <BaseContainer title="Jobs" navigation={this.props.navigation} scrollable refresh={this.refreshJobs}>
<Tabs renderTabBar={()=> <ScrollableTab />} tabBarUnderlineStyle={style.secondaryBackground}>
<Tab heading="In Progress" textStyle={style.tabTextStyle} activeTextStyle={style.activeTabTextStyle}>
{ this.renderInProgressJobs }
<Button full style={[style.secondaryBackground, style.newJob]}>
<Text>CREATE NEW JOB</Text>
</Button>
</Tab>
<Tab heading="Submitted" textStyle={style.tabTextStyle} activeTextStyle={style.activeTabTextStyle}>
{ this.renderSubmittedJobs }
</Tab>
</Tabs>
</BaseContainer>;
}
}
few mistakes here:
you can't re-assign another value to the observable variable, it'll destroy the observable. You need to mutate the observable. For example, you can directly assign values to existing properties or use extendObservable for assigning new properties to observable object.
If you use MobX < 4, adding new properties the observable object will not trigger changes because the properties are set when the observable object was created. extendObservable may work but it's also limited. Use observable Map instead.
#observer should be used for component class (or SFC), not member functions inside of the class

React native navigator passing parent component to child

I don't know how to pass a reference to the TripList instance below to the AddTrip component. I need to do something like that to signal to TripList to refresh the data after adding a new trip.
In my render() method, inside <Navigator> I have:
if (route.index === 1) {
return <TripList
title={route.title}
onForward={ () => {
navigator.push({
title: 'Add New Trip',
index: 2,
});
}}
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}}
/>
} else {
return <AddTrip
styles={tripStyles}
title={route.title}
onBack={() => { navigator.pop(); }}
/>
}
However, when I call onBack() in AddTrip, after adding a trip, I want to call refresh() on TripList so the new trip is displayed. How best can I structure things to do that? I'm guessing I need to pass TripList somehow to AddTrip and then I can call refresh() there easily right before calling onBack().
This is not how React works. You don't pass instances of a component around, rather you pass the data to your component via props. And your component AddTrip should receive another props which is a function to call when adding a trip.
Let me illustrate this with a code example, this is not how your code should be in the end, but it'll illustrate how to contain the data outside of your components.
// Placed at the top of the file, not in a class or function.
let allTrips = [];
// Your navigator code.
if (route.index === 1) {
return <TripList
trips={allTrips}
title={route.title}
onForward={ () => {
navigator.push({
title: 'Add New Trip',
index: 2,
});
}}
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}} />
} else {
return <AddTrip
styles={tripStyles}
title={route.title}
onAdd={(tripData) => {
allTrips = [...allTrips, tripData];
}}
onBack={() => { navigator.pop(); }} />
}
As you can see, the logic about adding and finding the trips comes from the parent component, which is the navigator in this case. You will also note that we are reconstructing the content of allTrips, this is important as React is based on the concept of immutability.
You must have heard of Redux which is a system allowing all your components to discuss with a global store from which you fetch and save all your application state. It's a bit more complex that's why I did not use it as an example it.
I'll almost forget the most important! You will not need to signal to to your component that it needs refreshing, the magic of React should take care of it by itself!