ali ali
ali ali

Reputation: 21

react-native: How can I update state in context api?

When I update the state in another component, I can't re-render of the Provider in ContextProvider.js and hence the consumer data doesn't change.

ContextProvider.js

export const AppContext = React.createContext({basketNumber:0});
export class ContextProvider extends React.PureComponent {
  state = {
      basketNumber:0,
  };

  render() {
    return (
      <AppContext.Provider value={this.state}>
        {this.props.children}
      </AppContext.Provider>
    );
  }
}

in another componet I call it:

import { AppContext } from './../../../components/ContextProvider';

export default class Example extends React.PureComponent  {
   static contextType = AppContext;
}

in example class, I can call basketNumber

this.context.basketNumber

But how can I update it?

I tried this:

this.context.basketNumber = 10;

//EDIT

I add this code to Context provider:

addNumber(number) {
    this.setState({basketNumber:id});
};

and try call it in example class:

this.context.addNumber(10);

But I get this error:

_this.context.addNumber is nut a function

Upvotes: 1

Views: 6494

Answers (2)

Daniel
Daniel

Reputation: 15413

You can set it up as a functional component here:

const AppContext = React.createContext();
export const ContextProvider = ({ children }) => {
  const basketNumbers = [
    { number: 0 },
    { number: 1 }
  ]
  return (
    <AppContext.Provider value={basketNumbers}>
      {children}
    </AppContext.Provider>
  )
}

export default AppContext;

Then in the other component you call it like so:

import React, { useContext } from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import AppContext from '../context/AppContext';

const IndexScreen = () => {
  const basketNumbers = useContext(AppContext);

  return (
    <View>
      <Text>Index Screen</Text>
      <FlatList data={basketNumbers} keyExtractor={(basketNumber) => basketNumber.number} renderItem={({ item }) => {
        return <Text>{item.number}</Text>
      }} />
    </View>
  )
};

const styles = StyleSheet.create({});

export default IndexScreen;

Now if you want to edit or change the data it will cause the application to re render which is dealing with state.

What I have is passing down a static list and using the Context api.

So you would need to initialize a piece of state inside your Provider.

So you need to pass an object with a couple of properties, maybe one called data which will have your array of basketNumbers. Your second property could be addBasketNumber: () => {}. It's a callback function.

So if that second component that I labeled Index Screen ever calls addBasketNumber, that will cause the Provider to randomly generate a new basketNumber and add it to the list of basketNumbers. So the basketNumbers will re-render, the entire application will re-render and the Index screen will get the brand new list of basketNumbers.

So you want to do the following to the first component that I turned into a functional component:

import React, { useState } from 'react';

const AppContext = React.createContext();
export const ContextProvider = ({ children }) => {
 const [basketNumbers, setBasketNumbers] = useState([]);

So by default you are going to have an empty array because you are going to start off with no basket numbers. Now setBasketNumbers is your setter and completely replaces whatever is in basketNumbers but since I already did mention having a callback function, you can add a helper function like so:

import React, { useState } from 'react';

const AppContext = React.createContext();

export const ContextProvider = ({ children }) => {
  const [basketNumbers, setBasketNumbers] = useState([]);

  const addBasketNumber = () => {
    setBasketNumbers([...basketNumbers]);
  };

So the goal of this is to use your setter to add in a new basket number to your basketNumber variable. What you are also doing with that helper function is creating a new array and not changing the original array of basketNumbers. So this [...basketNumbers] just means create a new array and inside of that new array take all the current basket numbers we have and add it to the array.

You then want to create a second argument to that setBasketNumbers() that would be an object with the property value of your new basket numbers being incremented by one. A ${basketNumbers.length + 1} maybe? Anyway, you will want to then pass that down into your Context and it should re render in your FlatList.

So something like this:

return (
    <AppContext.Provider value={{ data: basketNumbers, addBasketNumber }}>
      {children}
    </AppContext.Provider>
  )

So it seems like what you are trying to do is using or trying to use context to manage state. You don't use context to manage state, you use it to move data around.

I am using useState calls to manage state here. Context is not a replacement for managing state such as Redux.

Upvotes: 2

Muhammad Mehar
Muhammad Mehar

Reputation: 1055

To update context value.

Checkout here Dynamic Context

Or you can pass a function down through the context to update the context.

Checout here Updating Context from a Nested Component

Upvotes: 0

Related Questions