markdown 使用没有状态管理库的React和React Native

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown 使用没有状态管理库的React和React Native相关的知识,希望对你有一定的参考价值。

It’s common these day when React & React Native developers use State management library (like Redux). I’ve been using React & React Native for a while now and found out that Pure React is actually not bad. In this article I will share my way of doing things with React & React Native purely, without State management library (represented by Redux). 

For those of you who are struggling learning Redux, because of the overwhelming of the whole React/JSX/Babel/Webpack/Native Component/Native Module/.. and have to add Redux to the list just to solve some of React problems, or because of the high learning curve of Redux, I hope you find this article helpful.

## Some of React problems with State Management

Assuming you have some knowledge of React, I will jump right in the problems that most of us encoutered at the beginning of time learning React:
- Flow `pass data down, pass event up` makes us to pass data & function via props and it's hard to manage when amount of props gets huge. (Comunication between component Parent - Child  )
- Another problem when passing data & function via props is the comunication of components not in same tree component.
- Sync data between component that share common information.

Solution? Redux ? Not so fast. People often come to Redux as it's the only way. Redux solves these problems quite well, but it come with alot of things and they make it hard to learn and apply. In this article, I will share 2 ways of approach with some tools to use in each situations.

## Approach 1: Global Store - Single Source of Truth
Global Store is one of Redux's fundamental principles. It can be done only using React.  
### 1. Context
`context` is rarely mentioned when people talking about React. With `context` you can communicate between parent component and child components without passing data & function via props (and props of components in the middle). You can read more about `context` [here](https://reactjs.org/docs/context.html). The implementation flow with `context` will be:

1. Declare context in Highest Level Component (Call as Root )
2. Create a method in Root that can get/set `state`.
3. Pass method via `context`
4. Declare to receive `context` in Child Components and we just have to use this method to get/set `state` in Root. 
5. All necessary information will be store in `state` of Root. When it changes, Root will re-render and UI will be updated.

Example Code:
 https://gist.github.com/lequanghuylc/2d9c0ff12c6ac64557c0acb4a0987ff1

__Warming about using `context`__: Facebook doesn't encourage us to use context, because `It is an experimental API and it is likely to break in future releases of React`, and if we dont use it right, it would be a mess. But let's pause for a second and look at famous libararies:

- [`react-redux`](https://github.com/reactjs/react-redux/search?utf8=%E2%9C%93&q=context&type=) uses `context` (a tiny amount)
- [`react-router`](https://github.com/ReactTraining/react-router/search?utf8=%E2%9C%93&q=context&type=) uses `context`
- [`react-navigation`](https://github.com/react-navigation/react-navigation/search?utf8=%E2%9C%93&q=context&type=) use `context`
- Some [UI library](https://github.com/mui-org/material-ui/search?utf8=%E2%9C%93&q=getChildContext&type=) uses `context`

Although it's not much but `context` is still be used. You just have to find a way to use it right. While wrapping it inside Root and Child, you won't work directly with `context` (_this.store_ instead), and if `context` gets removed in future releases, we can easily update 2 files Child and Root (with other solution right below) and it's done.
### 2. Export method setState
This solution can totally be a replacement for `context`, and it's more powerful because it can communicate outside of React scope, between React Applications (in case you mix React with other frontend library in your website).
The implemention flow of `export method setState` will be:
1. Create a class BaseComponent
2. Declare a `static` variable for `BaseComponent`
3. In `componentWillMount`, refer that `static variable` to a new function that can get/set state
4. Rewrite that `static variable` so it can work with many instances. (instances can be managed via id)
5. Extends all component with `BaseComponent`, then you can import it and directly call that `static variable` to get/set state (ofcouse this component has to be mounted first).

With this solution, we can manage all components from anywhere. If we want to follow Global Store Principle, just manage Root component. 

Example code:
https://gist.github.com/lequanghuylc/128ad8d8822d00c632a26e2737a75b81

## Approach 2: Manage state in services

`service` simply is some kind of code running under UI (headless) to do some small task assigned to it. Using `service` help you separate app logic handling and app's UI. 

The implementation flow with `service` will be:

1. Don't handle logic in UI Component. 
2. Create `service module` that can run independently & headlessly. `service` can be use for implementing app features (like server API wrapper, sockets, local DB, native functions).
3. any data return from `service` will send to component to update UI. We can use `event listener` for that.

One example of `service` is [`firebase`](https://www.npmjs.com/package/firebase). When using `firebase database`, we dont know about `token` or something like that. Because `firebase` doesnt return this information and we doesn't need to know. Everything unnessary will be kept internally. `firebase database` only provide `data`, the way to CRUD `data` and some `listener` to notify when `data` is changed. 

We can totally build a `service` like that to communicate with Server via API. Here is some example of build a service, using `class` & `state` for React similarity.

```
class ServiceAPI {
	constructor() {
      this.syncData();
   	}

	state = {}; 
    // update state directlly using this.state.something = "something"

	syncData = () => {
       // sync data with local when re-open app
    }

	login = (id, pass) => {
      // call api or something
      // token will be stored in state
    }

	logout = () => {
      
    }

	onLoggedOut = ( callback ) => {
      // listener will run callback function when user logged out
    }
}

// remember to make it singleton when export (with new keyword)
export default new ServiceAPI();
```

You can also use directlly `event listener` to communicate between components. (Example: Convert Modal component Alert to function). Take a look at [react-native-event-listeners](https://github.com/meinto/react-native-event-listeners) & [js-events-listener](https://github.com/lequanghuylc/js-events-listener) (forked)

## Conclusion

2 Approaches in this article is mentioned theoretically. I will update example app as soon as possible, but I think it's kinda clear and easy to apply.

So what's about __Redux__ ?

__Redux__ is another story. As mentioned above, __Redux__ solves the problems quite well, but it comes with alot of things: Architecture (from `flux` to `redux`), new definitions when implement (`action` & `reducer`) and the whole ecosystem to develop `the Redux way`. I was just simply shared some tips & tools so you can do more with __Pure React__. You can use it in your own way.

If you know all about __Redux__ & its ecosystem. Well then, nothing keeping you from not using __Redux__. But keep in mind that we don't have to know all that to do the job. And the problems dont stay in libraries & tools, but in our way of using them.

Let's keep our minds open and think about these. :)

以上是关于markdown 使用没有状态管理库的React和React Native的主要内容,如果未能解决你的问题,请参考以下文章

react-redux状态管理思想

React-5-状态管理器Redux

React状态管理:react-redux和redux-saga(适合由vue转到react的同学)

使用React Context进行状态管理(五)Provider与Consumer匹配

redux状态管理和react的结合使用

react hooks的缺点(针对状态不同步和没有生命周期)