0525 0601 - React Component父子组件间传值, Redux Reducer, react/redux state, Kendo, ReactJson, Router, Jest

1. React Component父子组件间传值

1.1 Parent to Child — Use Prop

class App extends React.Component {
    render() {
    [... somewhere in here I define a variable listName which I think will be useful as data in my ToDoList component...]
        
        return (
            <div>
                 <InputBar/>
                 <ToDoList listNameFromParent={listName}/>
            </div>
        );
    }
}

现在在ToDoList这个component中,使用this.props.listNameFromParent即可拿到listName

1.2 Child to Parent — Use a callback and states

  1. Define a callback in my parent which takes the data I need in as a parameter.
  2. Pass that callback as a prop to the child (see above).
  3. Call the callback using this.props.[callback] in the child, and pass in the data as the argument.
// parent
class ToDoList extends React.Component {
	constructor(props) {
        super(props);
        this.state = {
            listDataFromChild: null
        };
    };

    myCallback = (dataFromChild) => {
    	this.setState({ listDataFromChild: dataFromChild });
        [...we will use the dataFromChild here...]
    };
    
    render() {
        return (
            <div>
                 <ToDoItem callbackFromParent={this.myCallback}/>
            </div>
        );
    }
}
// child
class ToDoItem extends React.Component{
    someFn = () => {
        [...somewhere in here I define a variable listInfo which I think will be useful as data in my ToDoList component...]
        this.props.callbackFromParent(listInfo);
    },
    render() {
        [...]
    }
};

ToDoList (parent) will now be able to use listInfo within it’s myCallback function!

1.3 Between Siblings — Combine the above

Not surprisingly, to pass data between siblings, you have to use the parent as an intermediary.

2. Redux Reducer

2.1 更新state

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}

We don’t mutate the state. We create a copy with Object.assign(). Object.assign(state, { visibilityFilter: action.filter }) is also wrong: it will mutate the first argument. You must supply an empty object as the first parameter. You can also enable the object spread operator proposal to write { …state, …newState } instead:

An alternative approach is to use the object spread syntax recently added to the JavaScript specification:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return { ...state, visibilityFilter: action.filter }
    default:
      return state
  }
}

2.2 Updating Nested Objects

2.2.1 updating state.first.second[someId].fourth might look like:

function updateVeryNestedField(state, action) {
  return {
    ...state,
    first: {
      ...state.first,
      second: {
        ...state.first.second,
        [action.someId]: {
          ...state.first.second[action.someId],
          fourth: action.someValue
        }
      }
    }
  }
}

看起来很复杂,所以:This is one of several reasons why you are encouraged to keep your state flattened, and compose reducers as much as possible.

2.2.2 Common Mistake #1: New variables that point to the same objects

Defining a new variable does not create a new actual object - it only creates another reference to the same object. An example of this error would be:

function updateNestedState(state, action) {
  let nestedState = state.nestedState
  // ERROR: this directly modifies the existing object reference - don't do this!
  nestedState.nestedField = action.data

  return {
    ...state,
    nestedState
  }
}

This function does correctly return a shallow copy of the top-level state object, but because the nestedState variable was still pointing at the existing object, the state was directly mutated.

2.2.3 Common Mistake #2: Only making a shallow copy of one level

Another common version of this error looks like this:

function updateNestedState(state, action) {
  // Problem: this only does a shallow copy!
  let newState = { ...state }

  // ERROR: nestedState is still the same object!
  newState.nestedState.nestedField = action.data

  return newState
}

Doing a shallow copy of the top level is not sufficient - the nestedState object should be copied as well. 应该newState.nestedState就还是state.nestedState

3. react state vs redux state

  1. React state is stored locally within a component. When it needs to be shared with other components, it is passed down through props.

用法: react component中,自己定义一个this.state,或者在hook function component中,用useState,得到的是react state

  1. When using Redux, state is stored globally in the Redux store. Any component that needs access to a value may subscribe to the store and gain access to that value.

用法:比如useSelector

4. Kendo - React中好看又简单的component库

4.1 input

4.2 dropdownlist

4.3 几点注意:

  1. 按要求下载一系列依赖包,否则可能会有各种dependency找不到的问题:
    npm install --save @progress/kendo-react-grid @progress/kendo-data-query @progress/kendo-react-inputs @progress/kendo-react-intl @progress/kendo-react-dropdowns @progress/kendo-react-dateinputs @progress/kendo-drawing @progress/kendo-react-data-tools
    有的的确用不到可以试着不下载或者从package.json里删了

  2. 记得按他的要求引入css

    • Default theme—Available through the @progress/kendo-theme-default NPM module.
    • Bootstrap theme—Available through the @progress/kendo-theme-bootstrap NPM module.
    • Material theme—Available through the @progress/kendo-theme-material NPM module.

    我只用到了1和3,如果加上2一些效果反而被bootstrap效果覆盖了:

    npm install --save @progress/kendo-theme-default
    npm install --save @progress/kendo-theme-material

    而且这里引入css比scss好,因为css是已经编译好的

    import '@progress/kendo-theme-default/dist/all.css';
    import '@progress/kendo-theme-material/dist/all.css';
    
  3. 本来一直在找input的onChange,基本找不到,基本都是onSubmit,但是试了试onChange也能用在kendo的floating label input上,只是后来想想,onChange的确不好,每次打一个字母,就会make一个新的request…但最后还是用了onChange…
    而且之前把onChange写成了onchange导致每次都是undefined的值传入…傻了

5. Every time react state changes, react component render will re-run

6. React display pretty json / React显示json的几个包

6.1 react-json-view

觉得这个最好,还能折叠
npm install --save react-json-view

6.2 react-json-pretty

npm install --save react-json-pretty

6.3 关于使用方法,和可能遇到的几个问题,见我另一篇博客

7. BroswerRouter vs ConnectedRouter

import { ConnectedRouter } from 'connected-react-router/immutable';
import { BrowserRouter as Router,} from 'react-router-dom';

Answer 1: ConnectedRouter is to be used with Redux and can synchronize router state with a Redux store.
BrowserRouter is the ‘standard’ React router for the browser, to keep the UI in sync with the current URL.

Answer 2: The main difference in connected-react-router/immutable in is that history is stored in redux as immutable object so you can time travel your history any time in the redux-life of the application.

所以我用了ConnectedRouter,不过没有试其他的可不可以
https://redux.js.org/advanced/usage-with-react-router

8. React router: exact path vs path

url redirect but new component not rendered in that page, still the old component: path -> exact path:之前遇到一个问题,就是react router中点击别的组件,发现url更新了,但是页面并没有更新,原因就是没有写成exact path,如果只是path,就会返回第一个匹配的url的component

<Route path="/users" component={Users} />
<Route path="/users/create" component={CreateUser} />

Now the problem here, when we go to http://app.com/users the router will go through all of our defined routes and return the FIRST match it finds. So in this case, it would find the Users route first and then return it. All good.

But, if we went to http://app.com/users/create, it would again go through all of our defined routes and return the FIRST match it finds. React router does partial matching, so /users partially matches /users/create, so it would incorrectly return the Users route again!

The exact param disables the partial matching for a route and makes sure that it only returns the route if the path is an EXACT match to the current url.

So in this case, we should add exact to our Users route so that it will only match on /users:

<Route exact path="/users" component={Users} />
<Route path="/users/create" component={CreateUser} />
Using exact path may throw error: Value must be set for boolean attributes.
Solved by:
You just need to add this to your tslint.json to suppress it: "jsx-boolean-value": false

8. How to combine a UI with a backend

9. npm ERR! code ELIFECYCLE

  • Step 1: $ npm cache clean --force

  • Step 2: Delete node_modules by $ rm -rf node_modules package-lock.json folder, or delete it manually by going into the directory and right-click > delete / move to trash. Also, delete package-lock.json file too.

  • Step 3: npm install

  • To start again, $ npm start (This step I didn’t use I remember)

10. React-state: useState

10.1

It is important to keep this in mind because, for example, if you want to update the state based on the new properties the component receives:

const Message= (props) => {
   const messageState = useState( props.message );
   /* ... */
}

10.2

Using useState alone won’t work because its argument is used the first time only — not every time the property changes (look here for the right way to do this).:

You must make use of useEffect hooks to implement, e.g.:

export const Persons = (props) =>  {

   const [nameState , setNameState] = useState(props)

   useEffect(() => {
       setNameState(props);
   }, [props])
   
   return (...)
   
}

useState doesn’t return just a variable as the previous examples imply.

It returns an array, where the first element is the state variable and the second element is a function to update the value of the variable:

const Message= () => {
   const [message, setMessage]= useState( '' );
}

This way, you can use the state variable in the functional component like any other variable:

const Message = () => {
  const [message, setMessage] = useState( '' );

  return (
	<div>
		<input
		    type="text"
		    value={message}
		    placeholder="Enter a message"
		    onChange={e => setMessage(e.target.value)}
		  />
	  	 <p>
	      <strong>{message}</strong>
	    </p>
	</div>
   
  );
};

11. Jest: get started

大约有两种方法,一种是写一个jest.config.js,一种是在package.json里写明‘jest’配置。

IntelliJ run的时候,会先找jest.config.js,没找到会继续找package.json里的‘jest’,如果还没找到就会用Jest里default的config。

jest.config.js example

我用的是package.json

jasmine vs jest

What is Jasmine?
DOM-less simple JavaScript testing framework. Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it’s suited for websites, Node.js projects, or anywhere that JavaScript can run.

What is Jest?
Painless JavaScript Unit Testing. Jest provides you with multiple layers on top of Jasmine.

12. karma

这一块我查了,但实际还没有用到

https://stackoverflow.com/questions/47329520/unit-testing-react-using-karma-and-jasmine

https://www.codementor.io/@kimagure/testing-reactjs-components-with-karma-and-webpack-8sdzi6hkf

13. 如果npm install不指明dev,会install devDependencies还是dependencies?

When you (or another user) run npm install, npm will download dependencies and devDependencies that are listed in package.json

首先,如果package json里有,那么那个包写在哪(devDependencies / dependencies),自然就会下载到哪

如果package json里没有,那么下载的时候必须 -save然后指明在哪,就会自动地把这个包添加到devDependencies或者dependencies。

如果package json里没有,下载的时候也不指明… 会下载但是不会加到package.json里。但是需要一个依赖包却不写到package.json这种情况,不太会遇到?

14. IntelliJ里直接run Jest文件

14.1 找不到jest config file

我用的是package.json里的jest,而不是jest.config.js:
Edit Configuration中,work directory应该是有package.json的目录,有的project根目录中的子目录可能才有package.json,这也是导致找不到config的原因

14.2 IntelliJ terminal可以运行jest,但是没法直接run

在package.json的scripts里写上:”test“:"jest",然后在IntelliJ terminal run: npm run test,就可以运行jest测试文件。

但是为什么在Edit Configuration里就总是找不到test文件呢:
No tests found when using jest

后来发现不是什么其它原因,就是那些Edit Configuration很容易写错,后来发现一个简单的方法:

每次Edit Configuration,+Jest后,直接在弹出来的配置框中选择All Test,然后在下方run的console右键那些run过的单个测试,然后点击run,就可以run成功,此时再点击IntelliJ的Edit Configuration,就可以看一下Edit Configuration到底该怎么写了。
而且有些describe里的测试,还是it测试,好像的确没法单个run,有时候还是得一个测试文件一个测试文件(spec.tsx)的为最小粒度地去run。

一些参考文档:
官方文档:
https://www.jetbrains.com/help/idea/running-unit-tests-on-jest.html

https://stackoverflow.com/questions/29904115/running-jest-tests-directly-in-intellij-idea-webstorm

15. cwd是什么

What’s the difference between

console.log(process.cwd())

and

console.log(__dirname);

process.cwd() returns the current working directory,
i.e. the directory from which you invoked the node command.

__dirname returns the directory name of the directory containing the JavaScript source code file

16. Tutorial: 如何用jest和enzyme对react进行测试

17. it vs test

In the docs it says here: it is an alias of test. So they are exactly the same.

test(name, fn, timeout)
// Also under the alias: 
it(name, fn, timeout)

// example
describe('rain', () => {
	test('did not rain', () => {
	  expect(inchesOfRain()).toBe(0);
	});
	
	it('did not rain', () => {
	  expect(inchesOfRain()).toBe(0);
	});
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值